Acumatica · Security

Acumatica GDPR — Data Subject Requests

How to handle Acumatica GDPR data subject access requests — what to extract, what to redact, and the workflows that comply with the 30-day SLA.

John Kihiu12 min read

Most of my Acumatica clients are in East and Southern Africa, so you might assume GDPR never comes up. It comes up constantly. The moment a distributor has one EU customer in the address book, or a Kenyan firm sells into Germany, someone eventually sends a data subject request — and Kenya's own Data Protection Act and South Africa's POPIA impose very similar obligations anyway. The good news is that Acumatica shipped a genuine GDPR toolkit back in 2018 R2, and most people running the system have never opened it.

This post walks through how I handle the three requests that actually arrive in practice — access ("show me what you hold about me"), erasure ("delete me"), and rectification ("fix this") — using the built-in tools plus a little customization for the fields the toolkit doesn't know about.

Where personal data actually lives in Acumatica

Before you can answer a request you need the map. In a stock instance, personal data concentrates in a handful of DACs: Contact (names, emails, phones — and remember every customer, vendor, and employee has a linked contact record), Address, BAccount, the EPEmployee chain, and then the long tail: CRActivity records (emails synced from Exchange are full message bodies), Note text, file attachments, and the audit trail if you have field-level auditing turned on for any of those tables.

That last one bites people. If you enable auditing on the Contacts screen, the AuditHistory table keeps old values forever — so "we deleted the email address" is not true until you deal with audit history too. The built-in pseudonymization handles this, which is one reason to use it rather than hand-rolled SQL.

The built-in toolkit: pseudonymize, not delete

Acumatica's answer to erasure requests is pseudonymization, not physical deletion, and that is the correct call for an ERP. A contact that placed orders is referentially welded to AR invoices, shipments, and GL history. Hard-deleting the row would either fail on foreign keys or corrupt your financial audit trail — which you are legally required to keep under tax law, and tax retention obligations generally override GDPR erasure.

The mechanism: DACs and fields that hold personal data are decorated with PXPersonalDataTable and PXPersonalDataField attributes. When you run the Pseudonymize action against a contact (available once the record's processing is restricted), Acumatica replaces every marked field value — in the live table and in audit history and the full-text search index — with a neutral placeholder, while keeping the record and all its financial links intact. There's a matching Restore option that works from an encrypted stash until you purge it, which covers the "we pseudonymized the wrong person" scenario.

Turn on the switch first

None of this appears in the UI until you enable GDPR Compliance Tools on the Enable/Disable Features screen (CS100000). It's off by default, and I've audited more than one instance where the team swore Acumatica "had no GDPR support" because nobody had ticked the box.

Custom fields: the part the toolkit can't see

Here's the gotcha from real projects: pseudonymization only touches fields marked as personal data. Every custom field you've added — the UsrNationalID on the customer, the UsrNextOfKinPhone on the employee extension — is invisible to it. If your DSAR process relies on the built-in action but your customizations added identity fields, you have a compliance gap that no screen will warn you about.

The fix is one attribute in your DAC extension:

C#
public sealed class ContactExt : PXCacheExtension<PX.Objects.CR.Contact>
{
    [PXDBString(20, IsUnicode = true)]
    [PXUIField(DisplayName = "National ID No.")]
    [PXPersonalDataField]   // now included in pseudonymization + audit scrub
    public string UsrNationalID { get; set; }
    public abstract class usrNationalID : PX.Data.BQL.BqlString.Field<usrNationalID> { }
}

Make this a standing rule in code review: any new field that can identify a person gets PXPersonalDataField the day it's created, not during the audit two years later.

Access requests: build the export once

For "show me everything you hold," I build a small set of Generic Inquiries keyed on ContactID: one over Contact/Address, one over activities and emails, one over cases and opportunities, and one over audit history for those tables. Export to Excel, review for third-party data (an email thread usually mentions other people — you're supposed to redact them), and send. The GI approach means the process is repeatable by an admin, not a developer, which matters because the GDPR clock is 30 days and I am not always available on day 28.

For portability requests, the same GIs exposed via OData give you a machine-readable format for free.

The process that holds up to scrutiny

Wrapping up

Acumatica handles the hard mechanical part of GDPR — scrubbing personal data from live tables, audit history, and search without breaking financial integrity — as long as you enable the feature and keep your custom fields annotated. The rest is process: verified requests, a case-based paper trail, and honesty about the downstream copies the ERP can't reach. Do the annotation work now; retrofitting PXPersonalDataField across three years of customizations under a regulator's deadline is a much worse afternoon.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.