---
title: "MJS Packaging: WooCommerce and ERP work | Nathan Gamache"
description: "MJS Packaging’s WooCommerce store: tax exemption logic, the plugin that exchanges order and customer data with their ERP, and a faster checkout."
url: https://www.nathangamache.com/projects/mjs/
---

# MJS Packaging

MJS Packaging sells packaging through a WooCommerce store that exchanges CSV files with their ERP system. I did not build the site; my part has been the systems behind it. I have worked on the store since 2024. I look after the stock importer, the customer syncer and the order exporter, reworked how the store decides who is tax exempt, led the sold-by-case functionality and pricing model, and done checkout, catalog and performance work around them.

[Visit mjspackaging.com](https://www.mjspackaging.com) · [All projects](https://www.nathangamache.com/projects/)

- Agency: **Perfect Afternoon**
- Years: **2024–2026**
- Work: **Tax exemption · ERP sync · Checkout · Upkeep**
- Stack: **WooCommerce · Tax logic · ERP sync · PHP**

![The MJS Packaging home page: a banner of blue-tinted plastic containers behind the line Your expert packaging solution partner, with Find packaging and Talk to an expert buttons](https://www.nathangamache.com/assets/img/projects/mjs.webp)

Tax

## Who is tax exempt

- **One status per company, 2025.** Exemption used to belong to each user. Now, when a user account is saved, its status is copied to every user with the same company number, so all the buyers at one company share it. I checked the change with a before and after export of the users.
- **Set from the ERP.** The tax-exempt column in the ERP’s daily customer list now sets exemption in the store’s customer sync, including for customers who create an account at checkout and match a record. When the checkout session does not carry the status through, a fallback re-runs the match. I also handled the ERP’s Y/N Charge Tax flag in the sync, and wrote test plans for the team.
- **Every 15 minutes, 2026.** A scheduled job syncs the ERP customer list, exemption data included, into the store.
- **Taxable unless matched.** Customers who do not match the ERP list default to taxable, and users already marked exempt are checked against the list again at checkout.
- **Better matching.** Refined how checkout customers are matched to ERP records, and confirmed it on production with a test checkout and the plugin’s logs.
- **TaxJar, January 2026.** In 2024 I compared Avalara AvaTax, TaxJar and WooCommerce Tax for the store. In 2026 I set up TaxJar to calculate state, county and city rates, and debugged how it worked with the exemption logic until the two agreed, then confirmed it with a live test order.
- **Explaining it.** Wrote up in the ticket threads how the exemption matching behaves, tracing a checkout through the plugin’s logs step by step.

ERP

## Orders and customers, to and from the ERP

- **PO numbers, live January 2026.** An optional PO field at checkout in place of the free-text order notes, stored as its own order field and written to a new po\_number column in the order file the ERP imports. The goal was less manual entry when orders are processed.
- **The import/export plugin, March 2026.** I reworked the agency’s custom plugin that exchanges CSV files between the store and the ERP. Customer sync and tax exemption now resolve in one matching pass. One 15-minute job replaced two, working through large files in slices, restarting when a file changes and guarding against overlapping runs. The order export got file locking, folder and permission checks, separate primary and backup files, logging for each item with a run summary, and more detailed failure emails. I added support for WooCommerce’s order storage tables (HPOS) with a legacy fallback, and an admin status screen.
- **Addresses that fit.** Capped address line 1 at 30 characters at checkout, so street addresses fit the ERP’s import field, and documented for the client which fields the order export sends.
- **Local pickup, 2025.** A Local Pickup option that appears only for ZIP codes within a set distance of the client, widened from 15 to 35 and then 50 miles. Pickup orders get an L at the start of the order number, so the ERP import can flag them, and the pickup details appear in the shipping option, the thank-you page, the order email and the FAQ.
- **Stock import.** With a teammate, made the plugin’s stock import from the ERP work under a server cron.

Store

## Checkout and catalog

- **A faster cart, June 2026.** Reworked the store’s custom shipping calculation, which runs on every cart update, so cart volume and package counts are worked out together in one cached pass, with its debug logging made optional and the shipping rules left as they were. Add to cart became a background request instead of a full page reload, which in my testing roughly halved the time a cart update takes, and checkout stopped asking carriers for live rates again unless the address or ZIP code changes. One setting turns all of it off.
- **Shipping.** A pallet charge applied only when an order ships by LTL freight, a shipping note in the cart and checkout, and the client’s corrected freight classes imported for stocked products.
- **Made in the USA.** A flag on products and variations, a badge with a small US flag on listings and product pages that updates when a shopper picks a variation, a matching filter option, and the client’s SKU list tagged in bulk.
- **Featured products.** The client’s chosen variations stay at the top of the shop page under every sort order, but not in filtered views or past the first page.
- **Case pricing.** I led the sold-by-case functionality and pricing model. The pricing table on case-sold products reads Case(s) (X units per case) and shows the cost per unit, and stocked closures, sprayers and pumps show a Full case quantity.
- **Google Merchant Center, 2026.** An automated product feed from the catalog. I moved WordPress cron to a server cron with more memory so the full feed could finish, made Call for Availability products say so instead of showing $0.00, and limited the feed to in-stock products with valid prices, 86 at launch. The SEO specialist registered it in Merchant Center, and we launched it together.
- **ActiveCampaign.** Connected the store and ran the historical order and catalog syncs, with the abandoned cart automations left off, as the client asked.

Upkeep

## Keeping it running

- An upgrade from PHP 8.0 to 8.4, staging first.
- Ten monthly maintenance cycles and eight monthly site audits: backup checks, updates with major versions tested on staging, image optimization and security scans, then Screaming Frog, GTmetrix and PageSpeed checks against LCP, TBT and CLS targets, form tests and a cross-device review.
- A site performance plan with prioritized recommendations and a technical SEO outlook in 2026, and its first phase: mobile PageSpeed Performance went from 47 to 91 and 93 on two test runs, and Accessibility, Best Practices and SEO each went to 100.
- Specified DMARC records for the store’s domain and its email sending subdomain, as part of improving delivery of the site’s form notifications.

## Related projects

### [Terminal Supply Co: Product Imports & AS400](https://www.nathangamache.com/projects/terminal-supply/)

More WooCommerce data work: a custom product importer, category-by-category imports, and an AS400 API with customer-specific pricing.

Technologies: WooCommerce, AS400, PHP, Data imports

[View project: Terminal Supply Co: Product Imports & AS400](https://www.nathangamache.com/projects/terminal-supply/)

### [SJD Taxi Booking Platform](https://www.nathangamache.com/projects/sjd-taxi/)

Business rules inside WordPress again, this time a booking system: cutoffs, round trips, settings for each site and Stripe payments.

Technologies: WordPress, PHP, TypeScript, React, Stripe

[Read the case study: SJD Taxi Booking Platform](https://www.nathangamache.com/projects/sjd-taxi/)

[View all 16 projects](https://www.nathangamache.com/projects/)

## Questions about this work?

I am happy to go into more detail on any of it. [nathanpaulgamache@gmail.com](mailto:nathanpaulgamache@gmail.com)

[Ask me about it](https://www.nathangamache.com/contact/)

---

Nathan Gamache · [nathanpaulgamache@gmail.com](mailto:nathanpaulgamache@gmail.com) · [GitHub](https://github.com/nathangamache) · [LinkedIn](https://www.linkedin.com/in/nathan-p-gamache/) · [nathangamache.com](https://www.nathangamache.com/)
