Multi-Module ERP Integration: Salesforce to Unanet
Industry -

Professional Services

Service -

ERP Integration

Frequency -

Every 30 minutes

Scale -

30-40+ records/day

Multi-Module ERP Integration: Salesforce to Unanet

Hours of manual data entry, multiplied across dozens of records per day, creates a compounding operational burden. When you’re managing project-based businesses—tracking accounts, contacts, projects, invoices, and purchase orders—the overhead of maintaining data across disconnected systems doesn’t just waste time. It delays billing, introduces errors, and creates synchronization problems that cascade through your financial operations.

We built an automated integration that moved a professional services firm from a costly legacy system to Unanet ERP, syncing five interconnected data modules with complex hierarchical dependencies and dynamic business logic—processing 30-40+ records daily without manual intervention.

The Challenge

A professional services firm was operating on an expensive ERP system that had increased pricing to unsustainable levels. They needed to migrate to Unanet, a modern ERP and PSA (Professional Services Automation) platform designed for project-based businesses. The previous system had some automation, but rebuilding that integration logic for Unanet while improving on the limitations was the core challenge.

Manual data entry wasn’t viable at their scale. With 30-40+ records flowing through daily—each requiring account setup, contact creation, project initialization with task structures, invoice generation, and purchase order processing—manual transfer would consume hours per record. Multiply that across their daily volume, and you’re looking at dedicating multiple full-time staff just to data entry.

The complexity extended beyond volume. These weren’t flat data transfers. Each record represented a hierarchical structure with dependencies:

  • Accounts had to exist before Contacts could be created under them
  • Contacts required complete address structures and field mappings
  • Projects couldn’t be initialized until the account and assigned project administrators existed
  • Projects required dynamic task structures based on organization type and project category
  • Invoices needed proper typing (Time & Materials vs Fixed Price) and configuration
  • Purchase Orders had conditional creation rules based on invoice type and organization-specific requirements

Miss a dependency, set an incorrect configuration code, or fail to map organization types properly, and the entire record would error out in Unanet. The integration needed to handle these dependencies correctly, validate configurations, and manage the sequential creation order—all while running autonomously every 30 minutes.

Our Approach

The core architectural challenge was building a system that could handle complex, hierarchical data flows with conditional business logic—without hardcoding organization-specific rules that would require redeployment every time business requirements changed.

We designed the integration around two key principles: dynamic configuration and template-driven behavior. Rather than embedding business rules directly in code, we built a system where business users controlled routing logic and task structures through Salesforce configuration and Unanet template projects.

Custom Salesforce Modules created the routing mechanism. We built custom objects in Salesforce that defined organization-specific behaviors—which projects required tasks, what invoice types to use, which purchase order rules applied. These modules had lookup relationships to accounts, allowing staff to assign configuration profiles at the account level. When a record processed, the integration queried these configuration modules to determine which business logic path to follow.

For example: If an account belonged to a specific organization type, the integration would check the linked configuration module. That module might specify “no default tasks” or “invoice-only, skip purchase orders” or “use custom approval workflow.” This approach meant adding new organization types or changing rules didn’t require code changes—staff updated the Salesforce configuration, and the integration adapted automatically.

Dummy Projects in Unanet solved the task structure problem. Rather than hardcoding task lists in the C# application, we created template projects in Unanet with complete task and subtask structures. Each template represented a project category (standard consulting engagement, fixed-price delivery, invoice-only arrangement, etc.). The integration queried these dummy projects, pulled their task structures, and duplicated them to new projects based on the Salesforce configuration module mappings.

When business requirements changed—new tasks needed, subtask modifications, different approval structures—staff updated the dummy project templates in Unanet and mapped the new template ID to the appropriate Salesforce configuration record. No code changes required. The next sync cycle picked up the new structure automatically.

The Solution

We built the integration as a C# .NET application deployed on the client’s Google Cloud Platform environment, running on a scheduled 30-minute execution cycle.

Authentication and Security used Google Cloud Secret Manager to store API credentials for both Salesforce and Unanet. The application pulled credentials at runtime rather than embedding them in configuration files. This approach centralized secret management and allowed credential rotation without redeployment.

One technical constraint required careful handling: Unanet’s authentication system invalidated existing tokens when issuing new ones. If the integration requested a new auth token while a previous execution was still running, the in-flight process would fail mid-operation. We implemented execution locking to ensure only one process ran at a time, preventing token invalidation issues.

Data Flow Architecture processed records in strict dependency order:

  1. Account Creation/Lookup: Search Unanet for existing account by identifier. If not found, create new account with complete organization details and type mappings.

  2. Contact Management: For each contact associated with the account, search Unanet by identifier. Create new contacts with full address structures, phone numbers, email addresses, and relationship mappings. Handle multiple contacts per account with proper parent-child relationships.

  3. Project Administrator Assignment: Before creating projects, map Salesforce user IDs to Unanet users via shared keys stored in custom Unanet user fields. Assign project administrators, managers, and approvers based on the Salesforce record’s creator and custom assignment fields. This ensures proper access controls and approval workflows in Unanet.

  4. Project Initialization: Create projects under the account with dynamic task structures. Query the appropriate dummy project template based on Salesforce configuration module mappings. Duplicate the template’s task and subtask structure to the new project. Set project type, billing method (T&M vs Fixed Price), and organization-specific flags.

  5. Invoice Generation: Create invoices under the project with proper typing and configuration codes. Invoice-only orders skip purchase order creation entirely based on Salesforce field values.

  6. Purchase Order Processing: For non-invoice-only orders, create purchase orders linked to invoices. Apply organization-specific rules from Salesforce configuration modules—certain organizations required specific approval flags, cost center codes, or custom processing workflows.

  7. Status Synchronization: Write success or error flags back to the originating Salesforce record. Success messages included Unanet project IDs and confirmation timestamps. Error messages captured specific failure points (missing required field, invalid configuration code, dependency not found) to enable quick troubleshooting.

API Request Handling used Polly, a .NET resilience library, for retry logic and fault tolerance. Rather than making direct API calls scattered throughout the codebase, we built a custom request wrapper that accepted standardized request models and handled platform-specific formatting (Salesforce REST vs Unanet’s API patterns). Polly managed retries with exponential backoff for transient failures—network timeouts, temporary API unavailability, rate limiting.

Custom Data Models normalized the different API schemas. We created C# models for every entity type in both platforms (Salesforce Accounts, Contacts, Custom Modules; Unanet Accounts, Contacts, Projects, Tasks, Invoices, Purchase Orders). Parent models defined shared structures; child models extended them with platform-specific fields. This abstraction layer isolated API changes—if Unanet modified their response format, we updated the models without touching business logic.

Error Handling and Logging operated at multiple levels. The application used structured logging (_logger.LogInformation, _logger.LogWarning, _logger.LogError) to capture execution details, API responses, and failure points. Logs were accessible through the Google Cloud console for post-execution review.

When a record failed—missing required field, invalid configuration code, dependency not met—the integration wrote the specific error message to a custom Salesforce field on the source record. Staff could see exactly what failed without needing to access server logs. Common errors included missing Unanet configuration codes, unmapped organization types, or incomplete contact address data.

Deployment and Testing followed a structured release process. We developed against Unanet staging APIs and used dummy accounts, organizations, and project codes for testing. Once changes passed validation in staging, we bundled the application as an executable and provided it to the client’s IT team for deployment to their production GCP environment.

When business logic changed, we made code updates, tested in staging, recompiled the application, and delivered the new executable for redeployment. The 30-minute execution schedule meant changes took effect quickly after deployment.

Some accounts triggered batch processing—a single Salesforce record might create one account with five to ten projects, each with complete task structures, invoices, and purchase orders. The integration handled these multi-project scenarios atomically, ensuring all related entities were created successfully or rolling back on failures.

Technical Complexity

The codebase reached approximately 10,000 lines of C# across models, business logic, API wrappers, and execution drivers. The data models alone consumed significant space—comprehensive representations of both platforms’ data structures with proper type handling, nullable field support, and relationship mappings.

Ten distinct business logic branches handled different organization types, project categories, invoice types, and purchase order rules. The dynamic configuration system meant these branches evaluated Salesforce configuration modules at runtime rather than following hardcoded paths.

The hierarchical dependency management was the most challenging aspect. Creating a project required:

  • Valid account (search or create)
  • Valid contacts under account (search or create multiple)
  • Mapped project administrators (user lookup via shared keys)
  • Appropriate task template (query dummy project, duplicate structure)
  • Correct configuration codes (validate against Unanet requirements)
  • Organization-specific flags (query Salesforce configuration modules)

Miss any dependency, and the entire chain failed. The integration had to detect failures at each step, determine if they were recoverable (retry with Polly), or terminal (write error to Salesforce and move to next record), and ensure partial states didn’t corrupt data integrity in either system.

Results

The integration eliminated what would have been hours of manual work per record across 30-40+ daily transactions. Staff went from planning dedicated data entry resources to having the system handle synchronization autonomously every 30 minutes.

Billing Cycle Improvement: Automated project and invoice creation meant billing could proceed immediately after projects were approved in Salesforce. The previous manual process introduced delays between project approval and invoice generation. The integration removed that gap.

Error Reduction: Manual data entry across five interconnected modules—with strict dependency requirements and validation rules—was error-prone. Transposition mistakes, missing required fields, incorrect configuration codes, and broken relationships created rework. The integration validated everything programmatically before submission, catching errors before they reached Unanet.

Configuration Flexibility: The dynamic routing system via Salesforce modules and Unanet dummy projects meant business users controlled behavior without requiring code changes. New organization types, modified task structures, and changed purchase order rules were configuration updates, not development projects.

Migration Completion: The integration successfully moved the firm from their expensive legacy system to Unanet, rebuilding and improving their previous automation while reducing operational costs.

Why This Works

Multi-module ERP integrations fail when they try to solve complexity through code volume. Hardcoding business rules, task structures, and organization-specific logic creates brittle systems that require developer involvement for every business change.

The dynamic configuration approach—custom Salesforce modules for routing logic, dummy Unanet projects for task templates—shifted control to business users while keeping the integration code focused on dependency management, validation, and error handling.

Hierarchical data flows require strict sequencing and comprehensive error handling. You can’t create a project without a valid account. You can’t assign a project administrator without first mapping users between systems. You can’t create purchase orders without understanding organization-specific rules. Building these dependencies into the architecture, with clear validation and rollback on failures, ensures data integrity across both platforms.

Ready to Integrate Your Systems?

Disconnected systems force manual work that wastes time and introduces errors. Our API Integration Services can unify your CRM, ERP, and business platforms—giving you automated workflows that handle complex data dependencies and conditional business logic.

Have a similar integration challenge? Let’s talk.

Let's Talk

Let's Solve Your Software Problem

We show up. On-site for discovery, kickoffs, training, and milestones—wherever the work takes us. No layers, no handoffs—just direct access to the engineers building your solution.

000 +

Proven Track Record

Organizations served across Texas and beyond

00 +

Integration Experience

APIs delivered across payment, inventory, and government systems

00 +

Years Experience

Enterprise software expertise our team brings to every project