Skip to content

Guides

Walkthroughs for the jobs that come up once B2B Pricing is running. Each one assumes you have been through the quick start.

Importing a supplier's price file

A supplier sends a file once a quarter. It goes in as one upload.

Go to Catalog → B2B Pricing → Price Lists and press the file button beside Add. The file has three columns (identifier, quantity, price), one line per price, with or without a header row:

identifier,quantity,price
ABC-1,1,10.00
ABC-1,12,9.00
ABC-2,,4.50
  • The identifier is matched against your products' Model or SKU, and you say which before you upload. Neither has to be unique in OpenCart, so an identifier that names no product, or names two, is refused rather than guessed at.
  • The quantity is the rung the price starts at. Leave it empty for the first rung, which is what a file with one price per product wants.
  • The price is a plain figure with a full stop. No currency symbol, and no thousands separator: 1,234 means different things in different countries and the import will not guess which you meant.

Import into a new list, and check it before anybody buys on it

Importing into a new list is the safe path, and it is the only rollback there is. That is why the form starts with it rather than offering it as a tick.

The list the import makes is a draft: it prices nothing for anybody, whatever assignments or dates exist, until you enable it. So:

  1. Import into a new list.
  2. Open it and read the rows. Public price today and Off are beside each one, so a supplier file whose prices have gone up reads as a negative Off rather than as a number you have to compare by hand.
  3. Enable it and move the assignment from the old list to the new one. The admin has no move control: add the same assignment on the new list, then delete it from the old one. An assignment made for All stores is one row per store, so delete each. Between the two steps the customer is on both lists and the cheaper price wins, so do both in one sitting. (The API's assignment endpoint does move one in a single call.)
  4. Delete the old list when you are happy.

Wait before that last step: while the old list is still there, moving back is the same add and delete the other way round.

Lines that did not land

Refused lines are counted, never written, and handed back as a file. A price against a product that does not exist prices nothing and would sit in the list forever being stepped over, so the import will not write one.

Press Download the refused lines after the import and you get your own lines back, in your supplier's spelling, with the reason beside each. Fix them and upload the corrected file into the same list: a line lands once however many times you send it, so re-running the whole file is safe and re-running only the corrections works too.

Replacing a list rather than editing one

Importing into a list customers are already buying on changes their prices as the file is read, and there is nothing to go back to. Import into a new list and move the assignment instead. It takes the same number of clicks, and it can be undone.

Pricing a whole category, or a whole brand, in one go

A supplier file is an import. A percentage off a range of your own is Bulk set, which is the button beside Import prices on Catalog → B2B Pricing → Price Lists and on Case Quantities.

It is two steps and a sentence.

  1. Which products. OpenCart's own three filters (product, category and brand), and the screen tells you how many products match as you narrow them. Leave all three empty to take the whole catalogue. Tick Include subcategories to take a category's children with it.
  2. What to write. Either a fixed price, or OpenCart price, less a percentage, into a price list at a quantity you name; or a minimum and a case size for a customer group.

Then read the sentence above the button before you press it. It is written with your own numbers in it, for example this writes 341 rows into Trade 2026 now, replacing 26 that already exist at quantity 1, and the count it names is the count that lands.

A percentage is worked out once, and then forgotten. What is stored is the resulting price, not the rule: change a product's OpenCart price tomorrow and the rows Bulk set wrote do not follow it. Run it again to bring them up to date. This is deliberate (see limits and guarantees), and it is why the rows it writes are ordinary rows you can read, edit and delete on the Rows tab afterwards.

Two things to know before you point it at a list people are buying on:

  • The category filter matches the category you picked and not its children, unless you tick Include subcategories. Unticked, OpenCart's own product list filters the same way, so the count on this screen and the count on Catalog → Products agree. Ticked, the sentence above the button names the category and says and its subcategories, and the count here is the only one that covers them.
  • A bulk write is not undoable as a unit. The safe path is the import panel's: Copy the list, write into the copy, read it, then move the assignment. The next guide walks through it.

Change a live list safely: copy, download, edit, re-import, move the assignment

A list customers are buying on is the one list you should not edit in place: every change reaches them as you make it, and there is nothing to go back to. Build the change beside it instead.

  1. Copy it. On Catalog → B2B Pricing → Price Lists, tick the list and press Copy. The copy is called the same with (copy) after it, has every price and quantity rung the original has, nobody on it, and starts switched off, so it prices nothing for anybody yet. Case quantities belong to a customer group rather than to a list, so there is nothing of them to copy.
  2. Download it. Open the copy, go to Rows, choose Model or SKU and press Download CSV. The file is identifier, quantity and price, one line per row, under a header naming model or sku, which is exactly what the import reads. Pick whichever identifier your catalogue keeps unique.
  3. Edit it in a spreadsheet or a text editor. Change prices, add lines for new products or rungs, save it as CSV. Deleting a line from the file does not delete the row from the list: the import only adds and replaces.
  4. Import it into the copy. Press the file button beside Add, choose Into a list I already have, pick the copy and upload. Any line that did not land comes back in the refused-lines file with the reason beside it. A Model or SKU that is empty, or shared by two products, is one of those, because the import will not guess which product you meant.
  5. Check it, then move the assignment. Read the copy's rows (Public price today and Off are beside each one), enable it, and move the assignment from the old list to the copy. Price check will tell you which list a customer is on now.

Keep the old list until you are sure. While it is still there, moving back is the same add-then-delete the other way round.

If the change is a percentage across a range rather than a file, step 2 to 4 is Bulk set into the copy instead, and the rest is the same.

Collecting a VAT number, and not charging tax on it

B2B Pricing holds no VAT number and decides no tax (see limits and guarantees). It does not need to: OpenCart already collects a VAT number on eleven screens and already stops charging tax to a customer group. What follows is five steps, all of them stock OpenCart, with nothing of this extension's in any of them.

  1. Make the field. Customers → Custom Fields → Add. Name it VAT number, set Type to Text, set Location to Account, and set Status to Enabled. That one field then appears on registration, on account edit, in the address book, at checkout, on the admin's own customer and address forms, on the order page, and through OpenCart's JSON API.

  2. Give it a format check. Paste this into Validation:

    /^[A-Z]{2}[0-9A-Z+*.]{2,13}$/

    It accepts the shapes European VAT numbers come in (NL123456789B01, DE123456789, BE0123456789, ATU12345678, FRXX123456789, IE1234567FA, EL123456789, SE123456789012, ESX1234567X, GB123456789) and refuses a missing country prefix, a lower-case prefix, an embedded space, something far too short, and pasted rubbish. OpenCart applies it on all four paths that can write the field, so a bad value cannot get in through the storefront, the admin or the API.

    It proves format, not entitlement, and it does not know each country's length or check digits. NL999999999B99 is the right shape and belongs to nobody. Treat a value that passes as a value worth checking, not as a checked value.

  3. Require it of the group that has to have one. Tick Required against your wholesale customer group and no other, on the custom field's own form. Retail customers are then never asked for it.

  4. Stop charging that group tax. System → Localisation → Taxes → Tax Rates, and on every rate, untick that customer group.

    Every matters, because of how OpenCart's own mechanism works: the exemption is the absence of a tick, rate by rate, and there is no this group pays no tax switch anywhere. A tax rate you add later is charged to that group until somebody unticks it there too, whether it is a new reduced rate, a second country or a rate a courier module installs. Put it on the list of things to check whenever a rate is added, and check the group's rates again after any upgrade that touches localisation.

  5. Move the customer into the group yourself, on their own form, once you are satisfied. This is the step that is deliberately manual. Nothing here moves anybody between customer groups automatically, because a format check is not a registration check, and a price that changed because a visitor typed fourteen characters of the right shape is a price nobody agreed.

Once they are in the group, everything else follows on its own: the group's price lists price them, the group's case quantities apply, and OpenCart charges them no tax.

If you already have the Import/Export extension

Your product discounts import through Import/Export and your price lists do not.

Core's own discount rows import that way: OpenCart's product_discount table is reachable through Import/Export's product fields, because a discount is part of a product record. A price list is not. It is its own thing, assigned to people, and one product has as many rows in it as it has quantity rungs, so a price ladder is many rows per product where Import/Export maps one source row to one record.

Because the work has a different shape, B2B Pricing carries the import above instead of adding a field to somebody else's, and it carries the way back out as well: Download CSV on a list's Rows tab writes the list in the same format the import reads, so a list can be edited in a spreadsheet and imported back without Import/Export reaching it at all. The steps are in Change a live list safely.

The two do not conflict: a product can carry core discounts and be on a price list at the same time, and importing one does not touch the other. Which of them a given customer pays is stated in limits and guarantees.

Asking a trade visitor to sign in

A visitor who has not signed in is priced at your store's default customer group, which is where OpenCart prices them and where this extension reads them too. So a list assigned to a customer by name, or to a trade group of its own, reaches nobody who has not signed in, and a list assigned to the default group reaches everybody, including the public. Keep trade prices on a group of their own and the two stay apart.

Either way B2B Pricing renders no words about a price list on a guest's page: no list name, no line totals on the ladder, not even a prompt to log in.

That is deliberate. A sign in for trade prices line is marketing, so it belongs in your own words, and a shop that sells to the public alongside the trade needs it in a different place from one that does not. Both options below are yours, and both already exist in OpenCart:

Hide prices from guests entirely. System → Settings → your store → Option → Login Display Prices. With it on, OpenCart prints no price to anybody who is not logged in, anywhere in the catalogue, and prints its own login or register to see prices line in the price block instead. Nothing of B2B Pricing's is needed and nothing of it interferes: your trade customers sign in and see their own prices, and everybody else sees a prompt.

Or leave prices public and invite the trade. Keep the setting off so the public buys at your public prices, and put the invitation where you control it: a banner on the home page, a line in the category description for the ranges you sell on contract, or an Open a trade account page linked from the footer. OpenCart owns all three and none of them is coupled to a theme.

Whichever you choose, a buyer who signs in gets their price on every surface in the store from the next page load, with no cache to clear: prices are resolved per request, after OpenCart's own product cache is read.