Skip to main content

Bukalapak Store Management

This is the installation and configuration guide for Bukalapak Store Management, the module you downloaded from your ECOSIRE dashboard. Everything on this page describes the bukalapak_store_management module exactly as it ships today — the facts below were read out of the released build, not from a roadmap.

Technical namebukalapak_store_management
Odoo versions17.0, 18.0, 19.0 (Community or Enterprise)
Current shipped version17.0 → 17.0.1.2.4, 18.0 → 18.0.1.2.4, 19.0 → 19.0.1.2.4
Price19.0 → $499 USD · 17.0 / 18.0 → $249 USD — one-time, per Odoo version
Odoo module licenceOPL-1
CategoryConnector
No activation, no licence key

This module ships licence-free: it installs and runs standalone, with no separate licence module to install and no activation key to paste. Your right to use it is the OPL-1 licence you purchased — a contract, not a configuration step. There is no Settings → ECOSIRE.COM menu and nothing checks a key at runtime.

Each Odoo major version is a separate product

The 17.0, 18.0 and 19.0 builds are separate purchases, and your dashboard offers the download matching the version you bought. Buy the version you actually run — and if you later upgrade Odoo, you need the build for the new version.

Requirements​

RequirementDetail
Odoo17.0, 18.0 or 19.0, Community or Enterprise. Self-hosted or Odoo.sh — Odoo Online (SaaS) cannot install third-party modules
Odoo appsbase, sale_management, stock, account, delivery, mail, web, portal, product, contacts, digest — Odoo installs any that are missing automatically
Python packagesrequests
Platform accountA Bukalapak seller account with API access — an app registered in the Bukalapak Developer Console, giving you a Client ID and Client Secret

Install the Python packages into the same interpreter that runs Odoo:

sudo -u odoo pip install requests

Installation​

1. Download the ZIP for your Odoo version​

Sign in at ecosire.com and open your dashboard downloads. You will only be offered the file matching the version you bought — that is expected, see the warning above.

2. Extract into your addons path​

unzip bukalapak_store_management_v19_*.zip -d /opt/odoo/addons/
ls /opt/odoo/addons/bukalapak_store_management/__manifest__.py # sanity check

The module must end up as a single top-level bukalapak_store_management/ directory with __manifest__.py directly inside it. If your ls check fails, the folder landed one level too deep or too shallow — move it so the path above resolves.

3. Restart Odoo and install​

sudo systemctl restart odoo
  1. Go to Apps and click Update Apps List (developer mode must be on).
  2. Search for Bukalapak Store Management and click Install.
  3. Odoo pulls in the Odoo apps listed above automatically. There is nothing else to install and no activation step — the app is ready to configure as soon as the install finishes.

When the install finishes, a Bukalapak menu appears in the main Odoo app switcher.

What changed in the current build​

If you last looked at this module a while ago, these shipped changes matter to you as a user:

Every sync log is readable, and the dashboard meets the contrast floor (since 1.2.4)​

A log the connector wrote is now visible to your team. The multi-company rule on sync logs was scoped through the log's optional queue job, so a log written outside a job — the scheduled importers (orders, customers, products, payouts, refunds and the cron runner) and the webhook receiver all write that way — matched nothing and was hidden from every user except your system administrator. Those are exactly the rows you need when an import fails, and a failed import could therefore leave no readable explanation behind. Logs now carry the company they belong to, so they stay correctly separated between your companies while remaining visible in the one that produced them. Your existing log history stays readable after the upgrade.

The dashboard's status colours meet WCAG 2.2 AA. The connection badge, the period-on-period deltas, the KPI badges and the activity timestamps were drawn in the same vivid green, orange and red used for the chart fills — between 1.9:1 and 3.1:1 against their own tint, and the muted timestamp text sat at 2.2:1 against white. Every one of those is now a dedicated darker (or, in dark mode, lighter) text colour measured against the background it sits on, so every figure on the dashboard clears the 4.5:1 contrast floor in light and in dark mode.

A failed dashboard load no longer logs as a JavaScript error. The dashboard catches a failed load and shows it (a banner, plus a notification when you refresh by hand), so it is a warning rather than an uncaught error — which matters if you or your developer ever open the browser console.

The dashboard is readable in dark mode and on a phone (since 1.2.3)​

Odoo serves dark mode as its own stylesheet bundle, and the dashboard was only listening for a theme attribute Odoo never sets — so a dark-mode user got a bright white dashboard. A dark stylesheet now ships in that bundle and re-themes every card, chart, banner and skeleton, so the dashboard follows the rest of your Odoo.

The Order Status chart draws. The doughnut was built empty and then updated, which left a legend sitting over a blank area even when you had orders. Charts are now drawn from the loaded figures after the page renders and take their colours from the active theme. A period with no orders says "No orders yet" in the chart instead of showing an empty legend that looks like a failure.

The first load, an empty period and a failed load each say what they are. The dashboard opens with a spinner and skeleton cards instead of zeros that read as real figures. A period with no data shows "No sales" or "No orders yet" in each chart, plus a banner that points you at Sync Now. A load that fails shows its message at the top of the page with Try again and Settings — previously the error sat below the fold under an all-zero dashboard, which was easy to mistake for a quiet month.

Lists read properly on a phone. Instance and order lists no longer carry an empty Active column, product columns use short headers that stay legible on a narrow screen with the sync status shown as a coloured badge, and long log messages truncate instead of stretching the row. The customer list gets the same short headers and status badge. A long store name is truncated to one line in the header instead of running off the screen, and Recent activity always shows its status dot.

Nothing about your configuration changes, and there is no re-sync to run — this release is presentation only.

Money is never converted at a guessed rate (since 1.2.2)​

An order, payout or analytics total whose currency Odoo could not price — an unknown currency, an archived one, or a rate missing for the order's own date — used to be converted 1:1 and then reported as though it were a real figure, so an Indonesian rupiah amount could be presented as USD. Those reads now refuse and say so instead of guessing, and the analytics conversion turns each order out of its own currency before it is added to a total.

The same release stops an order's money collapsing into its total: shipping, platform discounts, seller-funded and co-funded discounts, and shipping tax are each kept as their own figure, and a zero stays a stated zero rather than disappearing.

The marketplace order number is now on the order (since 1.2.2)​

The order mapping declared an order number, displayed it as the record's title, and never filled it in — so searching for the order you were looking at, by the number your Bukalapak seller centre shows, found nothing. It is now written on import, and the Bukalapak order id, the store it belongs to and the financial, fulfilment and tracking state are shown on the sale order and on the delivery, so an operator no longer leaves Odoo's own screens to find out which marketplace order they are working on.

Buyer details stay masked, and credentials stop leaking into evidence (since 1.2.2)​

Bukalapak masks some buyer contact details. The connector now stores exactly what the platform sent and records why the value is masked, so a masked value can no longer be mistaken for a real one and become a match key that quietly creates duplicate contacts.

The evidence store used to write provider error text and webhook bodies verbatim, which put bearer tokens and buyer e-mail addresses and phone numbers into any support bundle. Both the store and the generic webhook logger now redact at write time.

Re-imports update, they do not duplicate (since 1.2.2)​

Re-importing a product, an order or a return updates the record you already have. Identity is the marketplace id scoped to the instance it came from, never a guess, so two Bukalapak stores that happen to reuse an id keep their records apart.

OAuth state is now compared in constant time, and a tampered webhook signature is rejected. A product image reference that is not an HTTP URL is resolved through the documented media endpoint before it is downloaded, instead of being fetched as if it were a URL.

The dashboard, and what the module does not claim (since 1.2.2)​

The dashboard's end date is inclusive, and its loading and error states carry the ARIA roles a screen reader needs.

Nothing in the module advertises affiliate or creator attribution, or a creator refresh, because Bukalapak exposes no such feed: asking for one fails with a clear message rather than returning an empty result that looks like "no data".

One buyer is one contact (since 1.2.1)​

The order import and the customer refresh each used to decide for themselves who a buyer was: the order import matched on e-mail, then phone, then name, while the customer refresh matched on e-mail and phone only. A buyer the second one could not match was filed a second time, so running both over the same shop left you with duplicate contacts and the buyer's orders split between them.

Both imports now resolve a buyer through the same match, in the same order — e-mail first, then the phone's last ten digits, then a meaningful name — and a contact is created only when nothing matched. A customer refresh also recognises a buyer by name now, so a buyer whose Bukalapak profile carries neither e-mail nor phone is updated on the contact you already have instead of being created again.

If you already have duplicates from an earlier build, they are not merged automatically — check Bukalapak → Customers against Contacts and merge the pairs by hand, then let the scheduled actions keep them that way.

Wallet balances are no longer assumed to be in rupiah (since 1.2.1)​

A payout or transaction read that states no currency used to be booked in a fixed IDR. It now uses your company's own currency, and Bukalapak's currency code is used whenever the payload carries one.

Sync runs are fault-isolated (since 1.0.10)​

Every record the module handles during a sync — products, orders, customers, inventory, webhook-triggered imports — is processed inside its own transaction boundary. If Bukalapak sends one malformed or rejected record, only that record fails: it is written to Operations → Logs with the platform's own error text, and every other record in the run still imports and stays imported.

Before this change, a single bad record could abort the whole run — worse, it could silently roll back records that had already imported while the run's log still claimed success. If you used to re-run an import "to be safe" after a partial failure, that workaround is no longer needed: check Operations → Logs for the per-record verdict instead of re-importing on top.

No licence client, no activation (since 1.1.0)​

The module no longer declares the separate ECOSIRE licence-client module as a dependency, and there is no licence check anywhere in it. Concretely:

  • Fresh installs need one ZIP — the connector's. Nothing else to download, no key to activate, no Settings → ECOSIRE.COM screen.
  • If you are upgrading an older build that shipped with the licence client: the connector simply stops using it. You do not need to uninstall the licence client if other ECOSIRE modules on the same database still use it — this module just no longer touches it.

Configuration​

Connect to Bukalapak​

Open Bukalapak → Configuration → Instances and create a connection record. The fields the module defines for the connection are:

FieldTechnical nameTypeRequired
Instance NamenameCharYes
Client IDclient_idCharYes
Client Secretclient_secretChar (masked)Yes
Companycompany_idMany2oneYes
Access Tokenaccess_tokenChar (masked)No — filled in automatically when you connect, never typed by hand
StatusstateDraft / Connected / ErrorNo — maintained by the module

The fields marked Yes are enforced by the model — Odoo will not let you save the record without them. The technical name is the reliable identifier if your screen wording differs.

Press Connect in the header of the record. The module exchanges your app credentials directly with Bukalapak (the OAuth2 client-credentials grant against api.bukalapak.com/oauth/token, requesting the public, user, store, inventory, order and wallet scopes), stores the access token, and flips the record to Connected — no browser redirect is involved. The button hides itself once you are connected. Test Connection independently verifies the credentials; a failure marks the record Error and shows the reason in a notification that stays on screen until you dismiss it.

The same record carries three automation switches, all defaulting to off — auto_confirm_order, auto_create_invoice, auto_validate_invoice. They gate what the module is allowed to do automatically once connected: confirm imported orders, then create their invoice, then post it. Leave anything you are not ready for switched off, and turn them on one at a time.

Optional defaults on the same record, used when the corresponding feature runs: a default warehouse for order imports, a pricelist and salesperson for imported orders, a bank journal for payout statements, the products used to represent Bukalapak discounts and shipping charges on order lines, the risk-score thresholds behind the risk-detection scheduled action, and monthly marketing/operating cost figures that feed the net-profit figures in Analytics.

The module's own source contacts one host only: api.bukalapak.com. Your firewall must allow outbound HTTPS to it.

Public endpoints this module exposes​

The connector serves five externally reachable endpoints. The paths are relative to your Odoo base URL (web.base.url).

EndpointMethodAuthPurpose
/bukalapak/callbackGETPublic (unauthenticated)OAuth redirect target, for partner apps issued an authorization-code client by Bukalapak.
/bukalapak/webhook/productPOSTPublic (signature-verified)Inbound webhook receiver for product events.
/bukalapak/webhook/orderPOSTPublic (signature-verified)Inbound webhook receiver for order events.
/bukalapak/webhook/customerPOSTPublic (signature-verified)Inbound webhook receiver for customer events.
/bukalapak/webhook/refundPOSTPublic (signature-verified)Inbound webhook receiver for refund events.

The callback. The normal way to connect is the Connect button above — it needs no redirect URI. The callback endpoint exists for the case where Bukalapak issued your app an authorization-code client instead: add https://erp.example.com/bukalapak/callback as the redirect URI in your Bukalapak application, and make sure web.base.url is already set to that real HTTPS address, because the module builds the redirect URI from it during the token exchange. The endpoint is public because the browser arrives on it without an Odoo session, not because it is unguarded: it refuses a request with no authorization code, and matches the single-use state nonce issued when the flow started against the connection record before exchanging anything. On success it stores the token, marks the record Connected, and lands you back on the instance form.

The webhook receivers. Each receiver expects two headers on the request: X-Bukalapak-Shop-Domain, which tells the module which connection record the event belongs to, and X-Bukalapak-Hmac-SHA256, the signature of the raw request body. A request that passes verification is answered with {"status": "ok"} and gets a pending queue job in Operations → Queue Jobs — the Process Queue Jobs scheduled action then imports it like any other job. A request that cannot be resolved or verified is refused with {"status": "error", ...} and the attempt is written to Operations → Logs.

Registering webhooks with Bukalapak

Bukalapak has no API for registering webhook subscriptions automatically, so the Configuration → Webhooks screen in Odoo is a local registry: Create Webhook records the event type and delivery URL (your Odoo's receiver path from the table above) in Odoo, and you enter that same delivery URL manually in the Bukalapak seller/developer portal. The Test button sends a signed sample payload to the recorded URL so you can confirm the endpoint answers before you rely on it.

Scheduled actions​

The module installs 13 scheduled actions (Settings → Technical → Scheduled Actions):

Scheduled actionRuns every
Bukalapak: Sync Products30 minutes
Bukalapak: Sync Orders15 minutes
Bukalapak: Sync Customers60 minutes
Bukalapak: Sync Inventory10 minutes
Bukalapak: Process Queue Jobs5 minutes
Bukalapak: Import Payout Reports6 hours
Bukalapak: Import Refunds30 minutes
Bukalapak: Auto Risk Detection30 minutes
Bukalapak: Generate Analytics4 hours
Bukalapak: Webhook Health Check60 minutes
Bukalapak: Cleanup Old Logs24 hours
Bukalapak: Process Workflows10 minutes
Bukalapak: Daily Summary Report1 day

Intervals above are the shipped defaults. Adjust them to your volume — but be aware that the platform, not Odoo, sets the API rate limits, and shortening a sync interval is the usual cause of throttling errors in the logs.

Using the module​

The module's menu is laid out as:

  • Bukalapak → Dashboard
  • Bukalapak → Orders
  • Bukalapak → Refunds
  • Bukalapak → Products
  • Bukalapak → Customers
  • Bukalapak → Payouts
  • Bukalapak → Analytics
  • Bukalapak → Operations — Run Operations, Queue Jobs, Logs
  • Bukalapak → Configuration — Instances, Locations, Payment Gateways, Scheduled Actions, Webhooks

A normal first run is:

  1. Save the connection record, press Connect, and confirm the record shows Connected before importing anything.
  2. Review the mapping and default records (warehouse, pricelist, journal) so imported data lands on the right Odoo records.
  3. Run a small import first — restrict it by date or by a handful of products — and check the results before letting the scheduled actions take over.
  4. Watch the log records during the first full sync. Failures are recorded there rather than raised at the user, and since the fault-isolation change each failure names exactly the one record it concerns.

Troubleshooting​

SymptomCause and fix
The connection will not validateRe-check every required field in the table above. Most failures are a mistyped secret, or credentials created for a sandbox while the module points at production (or vice-versa).
Nothing syncs even though the connection is fineThe scheduled actions are disabled at the Odoo level, or Odoo's cron worker is not running. Check Settings → Technical → Scheduled Actions and that --max-cron-threads is greater than zero.
Sync starts then stops part-wayRead the module's log records for that run. Rate limiting and rejected field values are the two common causes; both are logged with the platform's own error text. Since the fault-isolation change, a stopped run means an infrastructure problem, not one bad record.
Records import but map to the wrong Odoo valuesFix the mapping records under the module's configuration, then re-run the import.
Duplicated products or customersProducts: run the initial import once — if a first attempt half-finished, check the existing records before re-running rather than importing on top. Customers: the order import and the customer refresh share one match since 1.2.1, so new duplicates should not appear; pairs created by an older build must be merged by hand (see One buyer is one contact above).
A webhook arrives but nothing imports, or the caller reports an errorThe request must carry both X-Bukalapak-Shop-Domain and a valid X-Bukalapak-Hmac-SHA256 signature over the raw body. A refused attempt is written to Operations → Logs with the reason.
The requests library is requiredrequests is missing from Odoo's Python environment. Install it into the interpreter that runs Odoo.

Version history​

Odoo versionVersion you download today
17.017.0.1.2.4
18.018.0.1.2.4
19.019.0.1.2.4

ECOSIRE module versions are <odoo major>.<module major>.<minor>.<patch>, so 19.0.1.2.4 is the Odoo 19 build of module version 1.2.4. All three series carry the same release number. Your installed version is shown in Apps.

The 19.0 build additionally carries the 1.2.0 expansion of the Bukalapak seller API — accepting and rejecting an order, pushing the AWB/tracking number, courier and shipping-rate reads, the category/attribute/brand and bulk listing operations, image upload, promotions, feedback reads and replies, bulk stock, the return refund step, and the finance and report reads. The 18.0 and 17.0 builds carry the 1.1.x repair history plus the fixes above.

Support​