First, a note on "compatibility" (for want of a better word). This piece is written after some extensive theming work in Ubercart 5.x-1.7. Much of this is still useful information for the Drupal 6.x version of Ubercart, but it may not be wholly accurate.
I should also note that this is not really a hand-holding step-by-step piece. It's more for people who are already pretty familiar with theming Drupal and just want to know where to look.
So, my main problem was my client wanted some pretty advanced theming doing on their cart and checkout pages. Fortunately most of this was possible with Ubercart, but there's quite a mix of things you need to know about before you can get really down and dirty with Ubercart theming.
Firstly there's TAPIr. This is Ubercart's own Table API, which as of Ubercart 6.x-2.x is now an extension of the Form API, but with Ubercart 5.x-1.x it is a standalone API. It draws heavily on the way the Form API works. I won't go in to too much detail, because it is already well documented here:
Suffice it to say, you need to read up and familiarise yourself with TAPIr if you need to add columns to tables presented by Ubercart (for example, you may need to add a column to show VAT on a product or perhaps show an additional unit price column in addition to the total column Ubercart shows by default).
Then there's the theming. With Drupal 5.x, and I think still with Drupal 6.x too, it's mostly done via theme functions and template.php. Not very designer-friendly, but usually granular enough to allow you to do most of what you'll likely need to. Someone sent me this page (I don't remember who or I'd acknowledge them) which lists the Ubercart 1.x theme functions, which was an excellent starting point:
But it doesn't really say which theme functions do what, so I'll save you the trouble and tell you the ones I needed:
Themes the credit card form pane on the checkout page. Useful for injecting additional credit-card logos, that sort of thing.
If you need to add classes to the address tables, or anything else in those panes, both billing and delivery addresses are rendered here.
Use this to theme the cart table on the checkout page. I needed this to make the checkout cart look *exactly* like the cart page.
This is actually the function that renders the wrapper mark-up for each pane on the checkout page. If you want to inject stuff in between the panes without really needing to write an entire separate pane, you can do it with this.
This is like theme_uc_cart_checkout_form, but for the review page. I used it to hide panes I didn't want in the review page (since it doesn't have independent settings) by switching using the pane title. Ugly, but effective and quick!
This is where the cart table is rendered (but remember, the correct way to alter the cart table itself is to use TAPIr) so you can control the mark-up surrounding the cart table.
And finally, this is the actual cart block itself. Use this to do stuff like change the appearance of the cart block by role (my use case) or anything else you might need to, naturally.
One big downfall is there does not seem to be a way to theme the panes on the review page. However, thill in the #drupal-ubercart channel on IRC (whom I'm told works for Commerce Guys, who did a most excellent Ubercart talk at DrupalCon Paris) suggested using this module, UC Optional Checkout Review to somewhat circumnavigate the issue by skipping the step entirely:
He made the point that (depending heavily on design) you can lose up to 20% of your orders by making people take that extra step. They either don't realise they're not finished or, in some cases, the rush of blood to the head on that expensive purchase has subsided so they have second thoughts.
You still have to theme the Review page, because if anything goes wrong with the card transaction folk still end up there, but I reduced the data on it down to just the "Billing address" and "Card details" panes using theme_uc_cart_checkout_review() and did some basic CSS so it didn't look too crappy. They can see all their order details again, if they need to, by hitting the back link. But you know what you ordered and people only see it if there is a card failure, so I just show them the card details so they can double-check.
That's about it, but don't forget, if you need to change strings of text and you can't quite get a handle on them there's always the String Overrides module, which can override any interface string that passes through the
t() function (which should be all of them!):