Qeasy Cloud
Get Started

Sales Contract Sync in Practice: A Single-Strategy Implementation from CRM to MySQL

· 陈洁琳· Integration Solutions· 30 views· 4 min read
MySQL纷享销客销售合同同步CRM到MySQL轻易云供应链集成单一策略增量全量

What This Strategy Solves

The sales contract is the source document of the supply chain. Once a contract is signed, downstream shipping, invoicing, and settlement all reference its number, amount, and customer code. In a real project with a retail enterprise, we found that the sales team signed contracts in the CRM, but the downstream warehouse and finance systems were still looking at hand-copied documents, with customer codes mismatched and contract amounts frequently off by a few cents. The root cause was not that any system was unusable, but that the contract master data did not have a stable synchronization channel. The goal of this strategy is straightforward: bring sales contracts from the CRM into a MySQL intermediate database in a traceable way, so that downstream systems can subscribe to it.

Data Flow and Field Mapping

The overall flow is CRM (source) → Qeasy Data Integration Platform (intermediate layer) → MySQL (target).

On the source side, we call the CRM's /cgi/crm/v2/data/query endpoint via POST, with the business object SaleContractObj. The operating user is passed through as an otherRequest parameter. Each field in the response body carries the _autoFillResponse marker, so the platform automatically flattens the response structure into addressable fields, sparing us from writing manual parsing scripts.

The intermediate layer does not require heavy processing. Its main job is code mapping and null-value fallback. A common pattern we see on the Qeasy platform is to maintain a single mapping table in the platform for code-type fields such as customer ID, customer name, and contract type, rather than hard-coding them in SQL.

The target is a MySQL table whose primary key is id and whose unique key is name (the system serial number). Writes are issued via SQL batchexecute. The key field mapping is as follows:

Target FieldSource (CRM)Notes
namenameSystem serial number, used as the unique key
field_contract_number__cfield_contract_number__cContract number, frequently used on the business side
account_numberaccount_idCustomer code, requires mapping
account_nameaccount_id__rCustomer name, related object
field_customer_status__cfield_customer_status__cCustomer status
field_contract_type__cfield_contract_type__cContract type

The source _id and the target id go through idCheck to ensure that the same contract is not inserted more than once.

How to Configure on Qeasy

On the Qeasy Data Integration Platform, this strategy corresponds to a single integration strategy: source is WebAPI (QUERY), target is SQL (EXECUTE). When configuring, we typically focus on four points:

  1. Request object: Pass SaleContractObj as dataObjectApiName in a fixed way. Do not change it temporarily inside the strategy, to avoid accidentally pulling the wrong object.
  2. Automatic response filling: Set autoFillResponse to true in the source metadata. The platform will automatically register returned fields as available source fields, so when you build the field mapping later you can pick them directly instead of typing JSONPath by hand.
  3. Target SQL template: Use batchexecute, with field values referenced via {{field_name}} placeholders. The primary key and unique key fields must appear in the template, otherwise incremental checks become unreliable.
  4. Centralized code mapping: For dictionary-style fields such as customer code and contract type that are defined on the business side, maintain a separate mapping table and reference it from a "value conversion" component on the platform. Change it in one place and it takes effect across all strategies.

Implementation Steps

We usually break a rollout into three phases to keep things steady:

  • Incremental starting point: On the first day the strategy goes live, run */10 * * * * (every 10 minutes) and pull only contracts that changed in the last 24 hours. This validates field mapping and the unique key. This step does not need to be full; the goal is to prove the channel works.
  • Full backfill: After incremental runs are stable for 1-2 days, trigger a one-time full backfill to catch up on historical contracts. Stop it immediately once finished, so it does not compete with incremental jobs.
  • Scheduling frequency: Set the source cron to */10 * * * * and the target cron to 3-59/10 * * * *. The source runs 3 minutes ahead of the target, so target writes always lag behind source reads. Even if a round is delayed, you will not get dirty data caused by "reading half and writing".

After go-live, we recommend keeping a week of runtime logs, reviewing failure reasons one by one, and configuring common rerun and backfill mechanisms directly on the platform.

Pitfalls and Lessons Learned

  1. Wrong unique key: At first we used the contract number as the unique key, but when a customer resigned a contract we would hit duplicate primary key errors. The safe approach is to use the system serial number name as the unique key, and keep the contract number as a regular business field.
  2. Missing customer code mapping: The CRM returns account_id for the customer ID, while fields with the __r suffix are related objects. If you do not distinguish these two types, the customer name field ends up being written verbatim into the customer code column, and downstream reports become a mess.
  3. Trying to sync header and lines at once: A contract has a header and lines. If you pull all line items in one shot, the field count explodes. The recommended approach is to sync the header first, then build a separate strategy for line items, and roll them out in phases so that issues are easier to locate.
  4. Mixing incremental and full loads: Running a full backfill at the same time as the incremental job causes the same row to be updated multiple times, leading to version confusion. We later agreed that full loads would only be triggered manually at night, and incremental jobs would only resume after the full load finishes.
  5. Operating user becomes invalid: The currentOpenUserId in otherRequest is tied to a specific user. When that user leaves or is deactivated, the interface returns empty and all subsequent calls fail. The safe approach is to use a dedicated integration account and to monitor its availability.

When to Use and When Not to Use

Applicable: The CRM is the contract source of truth; contract master data needs to be distributed to multiple downstream subscribers; freshness is required at the minute level; contract volumes range from tens of thousands to millions. Typical cases are retail, consumer goods, and manufacturing sales pipelines.

Not applicable: Scenarios where contracts need to be written back to the CRM (the CRM must also see downstream status); scenarios with complex approval flow backfills; line items with very large volumes that require second-level freshness; and scenarios where the CRM is not the contract-generating system.

Original content. Please credit the source when reposting: https://www.qeasy.cloud/insights/solutions/strat-mysql-p2d57ef-8267-mysql-edd0d04b

Comments