,

The Day a Simple CPF Mask Became a Production Problem (and What It Taught Me About Oracle PSRM)

Emerson Romão Avatar

At some point, every developer faces what looks like a “simple fix.”

This one started exactly like that. It was supposed to be a small improvement:

“We need to make sure CPF and CNPJ are saved without formatting.”

Sounds easy, right? A couple of replaceAll() calls, maybe a quick algorithm… done.

But as I’ve learned (and re-learned many times), in enterprise systems — especially in Oracle PSRM — nothing is ever just string manipulation.


Understanding the IDs Behind the Problem

Before diving deeper, it’s important to understand what these identifiers represent — especially if you’re not familiar with Brazilian systems.

CPF (Cadastro de Pessoas Físicas)

The CPF is a Brazilian individual taxpayer identification number issued by the Federal Revenue Service.

  • Assigned to individual persons
  • Used in banking, legal contracts, and tax reporting
  • Exemple format:
999.999.999-99

You can think of CPF as similar to the Social Security Number (SSN) in the United States.

CNPJ (Cadastro Nacional da Pessoa Jurídica)

The CNPJ is the identification number for legal entities (companies) in Brazil.

  • Assigned to businesses and organizations
  • Used for tax registration, billing, and legal operations
  • Exemple format:
99.999.999/9999-99

Conceptually, it is similar to an Employer Identification Number (EIN).

Passport

A passport is an internationally recognized identification document issued by a government.

  • Used mainly for foreign individuals without CPF
  • Alphanumeric format (varies by country)
  • Example format:
AB999999

Why This Matters

Although these identifiers serve different purposes, they all share one key role:

They act as unique identity keys across enterprise systems

And that’s exactly where the real problem begins — inconsistent formatting.


Where Things Started to Feel Off

The initial implementation worked perfectly.

For CPF, I tested:

999.999.999-99 → 99999999999 ✅

All good. Deployed to homologation. Confidence high.

Then came the first sign that something wasn’t right.

For CNPJ:

99.999.999/9999-99 → still stored with formatting ❌

Same logic. Same code. Different result.

That’s when I knew this wasn’t a “regex problem.”


The Moment of Realization

In enterprise systems, especially OUAF-based ones, there’s a hidden layer that many developers underestimate:

The Business Object structure defines behavior more than your code does.

So instead of continuing to tweak the algorithm, I shifted focus to the execution context. And that’s where everything clicked.


Two “Persons”, Two Different Worlds

Even though the system talks about “Person” as a single entity, internally it’s not that simple.

There are different Business Objects handling different flows:

  • C1-PersonIndividual → used for CPF
  • C1-PersonLegalEntity → used for CNPJ

Same concept, completely different pipelines. And guess what?

My algorithm was only attached to one of them.


Why This Was Subtle (and Dangerous)

This type of issue is tricky because:

  • Everything seems to work
  • Tests pass for one scenario
  • There are no errors in logs
  • The behavior is just… inconsistent

In reality, the system was doing exactly what it was configured to do.

I just hadn’t mapped the full picture.


The Fix Was Simple — After Understanding the Problem

Once I identified the root cause, the fix became obvious:

  1. Create a reusable normalization algorithm
  2. Attach it to both Business Objects:
    • ✅ Individual
    • ✅ Legal Entity

And most importantly:

👉 Run it during Pre-Processing

That guarantees:

  • Execution before persistence
  • Consistency across UI, integrations, and batch processes

The Final Algorithm

@Override
public void invoke() {        

    if (boInstance == null) {
        return;
    }

    COTSInstanceList idList = boInstance.getList(Elements.PERSON_ID_LIST);

    if (idList != null) {
        for (COTSInstanceListNode node : idList) {

            String idNumber = node.getString(Elements.PERSON_ID_NUMBER);

            if (idNumber == null || idNumber.trim().isEmpty()) {
                continue;
            }

            String idType = node.getString(Elements.ID_TYPE);
            String normalized;

            if (IdTypes.CPF.equals(idType)) {
                normalized = idNumber.replaceAll("[^0-9]", "");
            } else {
                normalized = idNumber.replaceAll("[^a-zA-Z0-9]", "");
            }

            node.set(Elements.PERSON_ID_NUMBER, normalized);
        }
    }    
}

Nothing fancy. But in the right place — it solves a real problem.


What This Really Taught Me

This wasn’t about CPF, CNPJ, Passport or regex. It was about understanding the system.

🔹 1. Context Matters More Than Code

You can write clean, correct logic…

…but if it runs in the wrong Business Object, it won’t work.

🔹 2. “It Works” Can Be Misleading

A fix that works for one flow doesn’t mean the problem is solved. Always validate:

  • All entity types
  • All entry points
  • All integration paths
🔹 3. Normalize Data Close to Persistence

Trying to fix formatting in UI or services is fragile. Doing it at the BO level:

✅ Centralizes logic
✅ Prevents bypass
✅ Guarantees consistency

🔹 4. Small Problems Can Scale Quickly

What starts as formatting inconsistency can lead to:

  • Duplicate records
  • Integration failures
  • Broken matching logic

All because of punctuation.


Final Result

After the fix:

✅ CPF stored without formatting
✅ CNPJ stored without special characters
✅ Passport handled correctly
✅ Consistent data across all flows


Closing Thought

This problem wasn’t really about formatting. It was about understanding how the system behaves under the hood.

And honestly? That’s what makes working with enterprise systems both frustrating — and interesting.


If You Work with Oracle PSRM / OUAF

Next time something “simple” doesn’t behave as expected…

👉 Don’t just debug your code
👉 Debug the execution context

That’s usually where the real answer is.

Join The Newsletter

Follow my journey building backend systems with Java and Oracle – sharing real challenges and solutions.

No spam. Unsubscribe anytime.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.