Setting up price updates on schedule from external sources for 1С-Bitrix
Manually updating prices in a catalog from supplier pricesheets is a daily routine for a manager, taking 30 minutes to several hours. Meanwhile, any delay means selling at outdated prices — either at a loss or losing the customer. Automatic scheduled price updates eliminate human error and reduce delay to minutes.
Price data sources
Suppliers provide prices in different formats:
- CSV/Excel — file on FTP, by link, or email. Most common format.
- XML (YML, CommerceML) — structured format, often with additional data (stock, descriptions).
- API — REST or SOAP endpoint. Most reliable option, but not available everywhere.
- 1С export — CommerceML files from 1С:Enterprise via standard exchange.
Each format requires its own handler, but the logic of updating prices in Bitrix is identical.
Price structure in Bitrix
Prices are stored in b_catalog_price table. Key fields:
-
PRODUCT_ID— catalog element ID. -
CATALOG_GROUP_ID— price type (retail, wholesale, purchase). Types are set inb_catalog_group. -
PRICE— numeric value. -
CURRENCY— currency (ISO code).
To update price via API:
\Bitrix\Catalog\PriceTable::update($priceId, [
'PRICE' => $newPrice,
'CURRENCY' => 'RUB',
]);
Or via old API if you need to create price when missing:
CPrice::SetBasePrice($productId, $newPrice, 'RUB');
Update algorithm
Step 1. Load pricelist. Script retrieves file from FTP (ftp_get()), downloads by URL (file_get_contents()), or requests supplier API.
Step 2. Parse. CSV is parsed via fgetcsv(), Excel via PhpSpreadsheet, XML via SimpleXMLElement. Result — array of "product identifier → price" pairs.
Step 3. Mapping. Supplier SKU is matched with Bitrix catalog element. Search by property PROPERTY_SUPPLIER_ARTICLE or XML_ID:
$element = CIBlockElement::GetList(
[],
['IBLOCK_ID' => $catalogIblockId, 'PROPERTY_ARTICLE' => $supplierArticle],
false,
['nTopCount' => 1],
['ID']
)->Fetch();
Step 4. Update. Write new price to b_catalog_price. When updating thousands of products, use direct SQL queries or batch D7 updates — element-by-element updates via CPrice::SetBasePrice() are too slow.
Markups and formulas
Supplier provides purchase price. Retail is calculated by formula:
- Fixed markup:
retail = purchase × 1.3(30%). - Progressive: markup depends on price range (cheap items — 50%, expensive — 15%).
- Rounding:
ceil($price / 10) * 10 - 1→ price 1,287 → 1,289.
Markup formulas are stored in config, not hardcoded. This lets managers change rules via admin interface.
| Purchase range | Markup | Example |
|---|---|---|
| Up to 500 ₽ | 50% | 300 → 450 ₽ |
| 500–5,000 ₽ | 30% | 2,000 → 2,600 ₽ |
| Over 5,000 ₽ | 15% | 10,000 → 11,500 ₽ |
Cron setup
Price updates run via cron:
# Load supplier A pricelist — daily at 6:00
0 6 * * * /usr/bin/php /home/bitrix/scripts/update_prices.php --source=supplier_a >> /var/log/price_update.log 2>&1
# Load supplier B pricelist — daily at 6:30
30 6 * * * /usr/bin/php /home/bitrix/scripts/update_prices.php --source=supplier_b >> /var/log/price_update.log 2>&1
Stagger updates by time — parallel import runs overload the database and can cause deadlocks.
Change control
Blind price updates are risky. Supplier pricelist error (price 100 instead of 10,000) causes losses. Safety mechanisms:
- Change threshold — if price changed more than 30%, don't update automatically, flag for manual review.
-
Logging —
price_update_logtable with fields: product, old price, new price, source, date. Allows rolling back erroneous updates. - Notification — email or Telegram notification to manager on anomalies.
$changePercent = abs($newPrice - $oldPrice) / $oldPrice * 100;
if ($changePercent > 30) {
logAnomaly($productId, $oldPrice, $newPrice, $source);
continue; // Skip update
}
Cache clearing after update
After bulk price update, clear catalog cache, otherwise users see old prices:
\Bitrix\Iblock\ElementTable::getEntity()->cleanCache();
BXClearCache(true, '/catalog/');
For sites with composite cache, additionally call \Bitrix\Main\Composite\Engine::deleteAllCache() or point cache clear for changed product pages.







