Two-Way Product Catalog Synchronization with 1C

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Implementing Two-Way Catalog Synchronization with 1C

Two-way synchronization is not just import and export separately. It's managing conflicts: what happens when price changed both in 1C and on website simultaneously? Who is source of truth for specific field? Without clear rules synchronization becomes chaos.

Defining Sources of Truth

First step — establish for each field which system is master:

Field Master Logic
Product name 1C 1C is nomenclature system
SKU / Article 1C Article set in accounting system
Description Website Marketing texts written in editor
Price 1C Pricing in accounting system
Stock 1C Actual warehouse accounting
Images Website Photos processed separately
SEO fields Website Meta title/description on website side
Active status Both 1C can delist, website too

Database Schema

CREATE TABLE products (
    id              BIGSERIAL PRIMARY KEY,
    onec_guid       UUID UNIQUE,           -- 1C identifier
    sku             TEXT,

    -- Fields from 1C (overwritten on each sync)
    name_1c         TEXT,
    price_1c        NUMERIC(12,2),
    stock_1c        INTEGER,
    active_1c       BOOLEAN DEFAULT true,

    -- Website fields (not overwritten by sync)
    description     TEXT,
    meta_title      TEXT,
    meta_description TEXT,
    images          JSONB,
    active_site     BOOLEAN DEFAULT true,

    -- Sync metadata
    last_sync_1c    TIMESTAMPTZ,
    sync_hash_1c    CHAR(64)              -- hash for change detection
);

-- Final status: product active only if active in both 1C AND website
CREATE VIEW products_active AS
SELECT * FROM products WHERE active_1c = true AND active_site = true;

Synchronization Algorithm: 1C → Website

class OnecToSiteSyncService
{
    public function sync(CommerceMLData $data): SyncResult
    {
        $result = new SyncResult();

        foreach ($data->products as $onecProduct) {
            $syncHash = $this->computeHash($onecProduct);

            $product = Product::firstOrNew(['onec_guid' => $onecProduct->guid]);

            // Skip if data unchanged
            if ($product->exists && $product->sync_hash_1c === $syncHash) {
                $result->skipped++;
                continue;
            }

            // Update ONLY fields from 1C (don't touch description, images, etc.)
            $product->fill([
                'sku'          => $onecProduct->sku,
                'name_1c'      => $onecProduct->name,
                'price_1c'     => $onecProduct->price,
                'stock_1c'     => $onecProduct->stock,
                'active_1c'    => $onecProduct->active,
                'last_sync_1c' => now(),
                'sync_hash_1c' => $syncHash,
            ]);

            $product->save();
            $result->updated++;
        }

        // Products 1C no longer exports — deactivate
        $syncedGuids = $data->products->pluck('guid');
        Product::whereNotIn('onec_guid', $syncedGuids)
               ->update(['active_1c' => false]);

        return $result;
    }
}

Synchronization: Website → 1C

Website sends to 1C only what changed on website and matters for 1C: new orders, returns, payment confirmations.

class SiteToOnecSyncService
{
    public function getUnsyncedOrders(): Collection
    {
        return Order::where('sent_to_1c', false)
                    ->where('status', '!=', 'draft')
                    ->with(['items.product', 'customer'])
                    ->get();
    }
}

Conflict Detection and Resolution

Conflict: active_site set to false by operator (delisted), but next 1C export contains active = true. Per rules table — 1C is master for active_1c, but active_site untouched.

Result: active_1c = true, active_site = false → product not displayed. Website operator retains control.

Monitoring Synchronization

-- Last sync statuses
SELECT
    source,
    COUNT(*) FILTER (WHERE status = 'success') AS success,
    COUNT(*) FILTER (WHERE status = 'error') AS errors,
    MAX(finished_at) AS last_run,
    AVG(EXTRACT(EPOCH FROM (finished_at - started_at))) AS avg_duration_sec
FROM sync_logs
WHERE started_at > NOW() - INTERVAL '7 days'
GROUP BY source;

Timeline

Two-way catalog synchronization with 1C, including testing on real configuration: 14–20 work days.