DynPricing Admin
{{ systemInfo.environment === 'production' ? 'cloud_done' : (systemInfo.environment === 'testing' ? 'science' : 'laptop') }} {{ systemInfo.environment_label || systemInfo.environment }}
{{ systemInfo.environment_note }}
{{ state.lastUpdated ? 'Updated: ' + state.lastUpdated : 'Loading...' }}
arrow_downward {{ queueStats.incoming_queues.znz_in.message_count ?? 0 }}
ZNZ
arrow_downward {{ queueStats.incoming_queues.elnino_in.message_count ?? 0 }}
ELNINO
|
{{ queueStats?.loading ? 'sync' : 'pending_actions' }} {{ queueStats?.message_count ?? 0 }}
INTRA
|
arrow_upward {{ stats.message_count }}
{{ routeName }}
warning
account_circle {{ currentUser.username || currentUser.name || 'User' }} ADMIN
warning MAINTENANCE MODE ACTIVE — {{ maintenanceMode.info?.reason || 'System maintenance in progress' }} (since {{ maintenanceMode.info?.enabled_at || 'Unknown' }})
Refreshing {{ currentTab.label.toLowerCase() }}...
error {{ state.error }}

Configuration Overview

Active projects with their strategies and rules.

Details Actions
{{ overviewIcon(row.type) }}
{{ row.label }} {{ row.subtitle }}
{{ row.meta }} --
info No active configuration available.
{{ state.selectedItems.projects.length }} selected
Visible Columns
rss_feed

Data Feed (Atom XML)

Access project data as an Atom XML feed. Use in RSS readers, Excel (=IMPORTXML), or any XML-compatible tool. The URL is secured with a personal token — no API key needed.

refresh Loading token...
open_in_new Open
Excel formula: {{ getExcelFormula('projects') }}
Regenerating invalidates all existing feed URLs
Other available feeds:
Order {{ col.label }} Actions
history
Showing {{ paginationBounds('projects').from }}-{{ paginationBounds('projects').to }} of {{ tableMeta.projects.total }}
Page {{ tableMeta.projects.page }} of {{ tableMeta.projects.pages }}
{{ state.selectedItems.strategies.length }} strateg(ies) selected
🎨 Order Project {{ t('tableSort.strategies.name') }} {{ t('tableSort.strategies.route') }} {{ t('tableSort.strategies.global_rule') }} Modified Actions
- {{ strategy.name }}
{{ route.name }}
{{ strategy.route || 'N/A' }}
{{ truncate(strategy.global_rule) || 'N/A' }}
{{ rule.name }} None
{{ strategy.is_active ? 'Active' : 'Inactive' }}
{{ strategy.modified_by }}
{{ strategy.modified_at ? formatDateTime(strategy.modified_at) : '' }}
-
history
Showing {{ paginationBounds('strategies').from }}-{{ paginationBounds('strategies').to }} of {{ tableMeta.strategies.total }}
Page {{ tableMeta.strategies.page }} of {{ tableMeta.strategies.pages }}
{{ state.selectedItems.rules.length }} rule(s) selected
🎨 Order Strategy {{ t('tableSort.rules.name') }} {{ t('tableSort.rules.expression') }} {{ t('tableSort.rules.filter') }} {{ t('tableSort.rules.status') }} Final Modified Actions
- {{ rule.name }} {{ truncate(rule.dsl_expression) }} {{ truncate(rule.filter_expression) || 'N/A' }} 🛑 Final -
{{ rule.modified_by }}
{{ rule.modified_at ? formatDateTime(rule.modified_at) : '' }}
-
history
Showing {{ paginationBounds('rules').from }}-{{ paginationBounds('rules').to }} of {{ tableMeta.rules.total }}
Page {{ tableMeta.rules.page }} of {{ tableMeta.rules.pages }}
shield

No guardrails found

Click "New Guardrail" to create your first reusable pricing constraint
{{ state.selectedItems.guardrails.length }} guardrail(s) selected
Order {{ t('tableSort.guardrails.name') }} {{ t('tableSort.guardrails.expression') }} {{ t('tableSort.guardrails.note') }} {{ t('tableSort.guardrails.status') }} Application Mode Modified Actions
{{ guardrail.name }} {{ truncate(guardrail.dsl_expression, 80) }} {{ guardrail.note || '-' }} {{ guardrail.application_mode === 'global' ? '🌐 Global' : guardrail.application_mode === 'filter' ? '🔍 Filter' : '📍 Local' }}
{{ guardrail.modified_by }}
{{ guardrail.modified_at ? formatDateTime(guardrail.modified_at) : '' }}
-
history
Page {{ tableMeta.guardrails.page }} of {{ tableMeta.guardrails.pages }}
Validity Global Actions
#{{ rule.id }}
{{ rule.domainLabel }}
{{ truncate(rule.domainDetail, 80) }}
{{ truncate(rule.targetSummary, 80) }}
{{ truncate(rule.targetDetail, 80) }}
{{ truncate(rule.actionSummary, 80) }}
{{ truncate(rule.actionDetail, 80) }}
{{ flag }}
{{ rule.updatedLabel || '?' }} {{ rule.validityLabel }} public Yes ? {{ rule.statusLabel }} {{ rule.ownerName || '?' }}
No rules found.
Showing {{ paginationBounds('singleRules').from }}-{{ paginationBounds('singleRules').to }} of {{ tableMeta.singleRules.total }}
Page {{ tableMeta.singleRules.page }} of {{ tableMeta.singleRules.pages }}
Actions
{{ tier.tier_name }} {{ tier.tier_remote_id }} {{ tier.project_domain || 'Shared' }} {{ tier.description }}
Showing {{ paginationBounds('pricingTiers').from }}-{{ paginationBounds('pricingTiers').to }} of {{ tableMeta.pricingTiers.total }}
Page {{ tableMeta.pricingTiers.page }} of {{ tableMeta.pricingTiers.pages }}
category
tag
touch_app
person
search
date_range
date_range
ID Entity Type Entity Name Action User Date & Time Actions
{{ log.id }} {{ log.entity_type === 'project' ? 'domain' : log.entity_type === 'strategy' ? 'hub' : log.entity_type === 'rule' ? 'rule' : log.entity_type === 'single_rule' ? 'rule_folder' : 'shield' }} {{ log.entity_type === 'single_rule' ? 'single rule' : log.entity_type }} {{ log.entity_name }} {{ log.action_type === 'create' ? 'add_circle' : log.action_type === 'delete' ? 'delete' : 'edit' }} {{ log.action_type }} person {{ log.user_name || 'Unknown' }} {{ formatDateTime(log.created_at) }}
No log entries found.
Showing {{ setupLogMeta.from }}-{{ setupLogMeta.to }} of {{ setupLogMeta.total }}
Page {{ setupLogMeta.page }} of {{ setupLogMeta.pages }}

API Key Usage Logs

ID API Key Endpoint Method Status IP Address User Agent Date & Time
{{ log.id }} {{ log.api_key_prefix || 'N/A' }} {{ log.endpoint }} {{ log.method }} {{ log.status_code }} {{ log.ip_address }} {{ log.user_agent || '-' }} {{ formatDateTime(log.created_at) }}
No API key usage logs found.
Showing {{ apiKeyLogsMeta.from }}-{{ apiKeyLogsMeta.to }} of {{ apiKeyLogsMeta.total }}
Page {{ apiKeyLogsMeta.page }} of {{ apiKeyLogsMeta.pages }}

📈 Sales Analytics

View sales statistics, turnovers, margins and missed revenue opportunities.

🏆 Top Products

View best selling products by quantity, revenue or order count.

Loading sales data...
Crunching numbers 📊
error {{ salesAnalytics.error }}
🏢 Domain: 🏆 Period: Sort by:
Total: {{ formatNumber(salesAnalytics.topProducts.totals.total_quantity) }} items | €{{ formatNumber(salesAnalytics.topProducts.totals.total_revenue_eur, 0) }} revenue
Loading top products...
# Product ID Product Name Domain Qty Orders Revenue € Avg Price € Margin € Margin % Last Sale Actions
{{ index + 1 }} {{ product.product_id }} {{ product.product_name || '-' }} {{ product.domain }} {{ formatNumber(product.total_quantity) }} {{ formatNumber(product.order_count) }} €{{ formatNumber(product.total_revenue_eur, 2) }} €{{ formatNumber(product.avg_price_eur, 2) }} €{{ formatNumber(product.total_margin_eur, 2) }} {{ product.margin_percent }}% {{ product.last_sale_date }} bug_report
inventory_2

No products found for the selected period.

📦 Total Orders
{{ formatNumber(salesAnalytics.summary.total_orders || 0) }}
💰 Revenue (incl. VAT)
€{{ formatNumber(salesAnalytics.summary.total_revenue || 0, 2) }}
💵 Revenue (excl. VAT)
€{{ formatNumber(salesAnalytics.summary.total_revenue_novat || 0, 2) }}
📋 Total Items
{{ formatNumber(salesAnalytics.summary.total_items || 0) }}
🛒 Total Cost
€{{ formatNumber(salesAnalytics.summary.total_cost || 0, 2) }}
📊 Gross Margin
€{{ formatNumber(salesAnalytics.summary.total_margin || 0, 2) }}
📈 Margin %
{{ salesAnalytics.summary.margin_percent || 0 }}%
💰 Left on Table (31d)
€{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.left_on_table || 0, 0) }}
{{ salesAnalytics.missedRevenue.summary?.last_31_days?.cheaper_count || 0 }} orders
📉 Position Penalty (31d)
€{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.position_penalty || 0, 0) }}
{{ salesAnalytics.missedRevenue.summary?.last_31_days?.expensive_count || 0 }} orders
Loading revenue data...
domain_disabled

No domain data found for the selected period.

Domain Orders Revenue Rev. (excl. VAT) Cost Margin Margin % Missed €
{{ domain.domain }} {{ formatNumber(domain.order_count) }} €{{ formatNumber(domain.revenue, 2) }} €{{ formatNumber(domain.revenue_novat, 2) }} €{{ formatNumber(domain.cost, 2) }} €{{ formatNumber(domain.margin, 2) }} {{ domain.margin_percent }}% €{{ formatNumber(getDomainMissedRevenue(domain.domain), 0) }} -
TOTAL ({{ salesAnalytics.domainStats.length }} domains) {{ formatNumber(salesAnalytics.domainStats.reduce((a, b) => a + b.order_count, 0)) }} €{{ formatNumber(salesAnalytics.domainStats.reduce((a, b) => a + b.revenue, 0), 2) }} €{{ formatNumber(salesAnalytics.domainStats.reduce((a, b) => a + b.revenue_novat, 0), 2) }} €{{ formatNumber(salesAnalytics.domainStats.reduce((a, b) => a + b.cost, 0), 2) }} €{{ formatNumber(salesAnalytics.domainStats.reduce((a, b) => a + b.margin, 0), 2) }} {{ salesAnalytics.summary.margin_percent || 0 }}% €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.missed_revenue_lowest || 0, 0) }}
💰 Left on Table (31d)
... {{ morphLeftOnTable31d }}
{{ salesAnalytics.missedRevenue.summary?.last_31_days?.cheaper_count || 0 }} orders ({{ calcOrderPercent('cheaper') }}%) where we're CHEAPER
= sum of (lowestPrice − ourPrice)
📉 Position Penalty (31d)
... {{ morphPositionPenalty31d }}
{{ salesAnalytics.missedRevenue.summary?.last_31_days?.expensive_count || 0 }} orders ({{ calcOrderPercent('expensive') }}%) where we're EXPENSIVE
= €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.total_price_diff || 0, 0) }} price diff × 25%
📊 Today
... {{ morphToday }}
💰 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.today?.left_on_table || 0, 0) }} | 📉 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.today?.position_penalty || 0, 0) }}
📈 Last 7 Days
... {{ morphLast7Days }}
💰 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_7_days?.left_on_table || 0, 0) }} | 📉 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_7_days?.position_penalty || 0, 0) }}
📉 Last 31 Days
... {{ morphLast31Days }}
💰 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.left_on_table || 0, 0) }} | 📉 €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.position_penalty || 0, 0) }}
🔄 Live Feed
{{ morphLiveFeed }}
● Auto-refresh ON ● Paused
🔴 Live Feed
Loading live feed with competitor analysis...
Time Order Product Domain Our Price Lowest Comp. Median Diff (Lowest) Missed € Compare
No orders with competitor data found. Try increasing the record limit or uncheck the filter. No recent orders with competitor data found.

📉 Daily Missed Revenue (EUR) Total: €{{ formatNumber(salesAnalytics.missedRevenueDaily.reduce((a, b) => a + b.missed_eur, 0), 0) }}

{{ day.date.substring(5).replace('-', '/') }}
Date Orders Items Revenue Rev. (excl. VAT) Cost Margin Margin % Missed €
{{ day.date }} {{ formatNumber(day.order_count) }} {{ formatNumber(day.item_count) }} €{{ formatNumber(day.revenue, 2) }} €{{ formatNumber(day.revenue_novat, 2) }} €{{ formatNumber(day.cost, 2) }} €{{ formatNumber(day.margin, 2) }} {{ day.margin_percent }}% €{{ formatNumber(getMissedForDate(day.date), 0) }} -
No data for selected period.
Last 100 orders, sorted by date descending
Loading orders...
Date/Time Order ID Domain Items Total Cost Margin Missed € Status
{{ formatDateTime(order.datum) }} {{ order.oid }} {{ order.domain }} {{ order.item_count }} €{{ formatNumber(order.order_total, 2) }} €{{ formatNumber(order.total_cost, 2) }} €{{ formatNumber(order.margin, 2) }} ({{ order.margin_percent }}%) €{{ formatNumber(order.missed_revenue, 2) }} - {{ order.status_label }}
No orders found.
🏢 Domain: 🏆 Period: Sort by:
Total: {{ formatNumber(salesAnalytics.topProducts.totals.total_quantity) }} items | €{{ formatNumber(salesAnalytics.topProducts.totals.total_revenue_eur, 0) }} revenue
Loading top products...
# Product ID Product Name Domain Qty Orders Revenue € Avg Price € Margin € Margin % Last Sale Actions
{{ index + 1 }} {{ product.product_id }} {{ product.product_name || '-' }} {{ product.domain }} {{ formatNumber(product.total_quantity) }} {{ formatNumber(product.order_count) }} €{{ formatNumber(product.total_revenue_eur, 2) }} €{{ formatNumber(product.avg_price_eur, 2) }} €{{ formatNumber(product.total_margin_eur, 2) }} {{ product.margin_percent }}% {{ formatDateTime(product.last_sale) }} 🔧 Debug -
No products found for the selected period.
Actions
{{ tier.tier_name }} {{ tier.project_domain || 'Shared' }} {{ displayNumber(tier.price_100) }} {{ displayNumber(tier.price_200) }} {{ displayNumber(tier.price_300) }} {{ displayNumber(tier.price_400) }} {{ displayNumber(tier.price_10000) }}
Showing {{ paginationBounds('marginLevels').from }}-{{ paginationBounds('marginLevels').to }} of {{ tableMeta.marginLevels.total }}
Page {{ tableMeta.marginLevels.page }} of {{ tableMeta.marginLevels.pages }}
Actions
{{ route.name }} {{ route.route || 'N/A' }} {{ route.description || 'N/A' }} {{ route.endpoint_url || 'N/A' }} {{ route.is_active ? 'Active' : 'Inactive' }}
Showing {{ paginationBounds('pricingRoutes').from }}-{{ paginationBounds('pricingRoutes').to }} of {{ tableMeta.pricingRoutes.total }}
Page {{ tableMeta.pricingRoutes.page }} of {{ tableMeta.pricingRoutes.pages }}
label

No dynamic tags found

Click "New Dynamic Tag" to create a tag that will be assigned to products matching a filter expression
{{ state.selectedItems.dynamicTags.length }} dynamic tag(s) selected
Order {{ t('tableSort.dynamicTags.name') }} {{ t('tableSort.dynamicTags.expression') }} {{ t('tableSort.dynamicTags.note') }} {{ t('tableSort.dynamicTags.status') }} Modified Actions
{{ tag.name }} {{ truncate(tag.dsl_expression, 80) }} {{ tag.note || '-' }} {{ tag.is_active ? 'Active' : 'Inactive' }}
{{ tag.modified_by }}
{{ tag.modified_at ? formatDateTime(tag.modified_at) : '' }}
-
history
Page {{ tableMeta.dynamicTags.page }} of {{ tableMeta.dynamicTags.pages }}
download Download Template
{{ selectedRrpItems.length }} items selected
Date Domain Tag EAN Abra ID Price Currency Type User Active Actions
{{ formatDate(item.datum) }} {{ item.domena || 'N/A' }} {{ item.tag || '-' }} {{ item.EAN }} {{ item.produkt }} {{ item.cena }} {{ item.mena || 'CZK' }} lock {{ item.typ === 'fixed' ? 'Fixed Price' : (item.typ === 'rrp' ? 'RRP' : 'Manual') }} {{ item.user || item.uzivatel || 'N/A' }} {{ item.aktivni == 1 ? 'Active' : 'Inactive' }}

Import RRP Data from XLSX

Step 1: Select Import Options

Import Mode:

Note: Your file can contain both RRP and Fixed prices (use 'type' column: rrp/fixed)
cloud_upload

Click to select XLSX file or drag & drop

Supported formats: .xlsx, .xls
description {{ selectedRrpFile.name }} Processing... {{ state.rrpDatabase.error }} Ready for column mapping
Analyzing Excel file...

Step 2: Map Columns to Database Fields

{{ state.rrpDatabase.totalRows || 0 }} rows detected in Excel file
No columns detected - please check Excel file format
{{ state.rrpDatabase.fileColumns.length }} columns found
Found columns: {{ column.name }},

No columns detected

The Excel file appears to be empty or doesn't have a proper header row. Please ensure:

  • The first row contains column headers
  • The file is in .xlsx or .xls format
  • The file is not corrupted

Import Preview - First 10 Products

Total: {{ state.rrpDatabase.previewData.length }} RRP: {{ state.rrpDatabase.previewData.filter(row => !row.type || row.type === 'rrp').length }} Fixed: {{ state.rrpDatabase.previewData.filter(row => row.type === 'fixed').length }} Will Create: {{ state.rrpDatabase.previewData.filter(row => row._exists && row._exists.action === 'create').length }} Will Update: {{ state.rrpDatabase.previewData.filter(row => row._exists && row._exists.action === 'update').length }}
Product Name EAN/ID Domain Price Type Tag Action
{{ row.name || '-' }}
? Has validation errors
{{ row.product_id || '-' }} {{ row.domena || '-' }} {{ row.price || '-' }} {{ row.currency }} lock {{ row.type === 'fixed' ? 'Fixed' : 'RRP' }} {{ row.tag || '-' }}
{{ row._exists.action === 'update' ? 'Will Update' : row._exists.action === 'create' ? 'Will Create' : row._exists.action === 'verify' ? 'Needs Review' : '-' }}
{{ row._exists.reason }}
Status Legend: Found in DB = Product exists, will update ? Similar Found = Similar product found, verify before import ? New Product = Will create as new product

RRP Prices Detected - Create Rules?

info
Only Fixed Prices Detected

Your import contains only fixed prices. These will be imported directly without creating rules.

Single Rules Preview

What will happen:

  • Import prices to database
  • Automatically create/update single pricing rules from RRP data (Fixed prices won't create rules)
  • Rules will be grouped by Tag + Domain combinations
  • All rules will be created in inactive state for safety
  • ? You can activate and configure them in Single Rules section after import

Rule Strategy: RRP-based pricing

Estimated Rules: {{ new Set(state.rrpDatabase.previewData.filter(row => (!row.type || row.type === 'rrp') && row.tag && row.domena).map(row => row.tag + '|' + row.domena)).size }} rules (based on unique Tag + Domain combinations from RRP data)

Step 3: Import Complete

Successfully imported {{ rrpImportResult?.imported || 0 }} RRP records.

Single Rules Created/Updated:

  • {{ rule.action === 'created' ? 'Created' : 'Updated' }} rule "{{ rule.tag }}" ({{ rule.product_count }} products, {{ rule.sale_percent }}% discount) - Global

All rules are inactive by default. Activate them in Single Rules section.

? RRP prices imported successfully. No single rules were created.

Single Rules Creation Warning

RRP prices were imported successfully, but there was an issue creating single rules:

{{ rrpImportResult.single_rules_error }}

Product Repricing Logs

Inspect recent MySQL output or long-term BigQuery history for a single product.

bug_report Debug Console
link GET {{ productLogEndpointPreview }}
error {{ productLogs.error }}
Loading product logs...
history

Recent Repricing Logs for {{ productLogs.domain }}

{{ productLogs.recentLogs.length }} entries
No recent logs found for this domain.
Product ID Price (VAT) Buy Margin Strategy Warehouse Date Sent Deliv.
{{ item.product_id }} {{ formatCurrency(item.price, productLogs.meta?.currency || item.currency || 'CZK') }} {{ formatCurrency(item.buy, productLogs.meta?.currency || item.currency || 'CZK') }} {{ typeof item.margin === 'number' ? item.margin.toFixed(1) + '%' : '-' }} {{ item.strategy || '-' }} {{ item.warehouse || '-' }} {{ item.recorded_at || '-' }} check_circle schedule verified pending open_in_new
Entries: {{ productLogs.meta.count }}
Source: {{ productLogs.meta.scope === 'long' ? 'BigQuery history' : 'MySQL recent logs' }}
Domain: {{ productLogs.meta.domain }}
Limit: {{ productLogs.meta.limit }}
Fetched: {{ productLogs.meta.retrieved_at }}
Open endpoint
Average sell {{ formatCurrency(productLogStats.avgSell, productLogStats.currency) }}
Average buy {{ formatCurrency(productLogStats.avgBuy, productLogStats.currency) }}
Average margin {{ formatPercent(productLogStats.avgMargin) }}
Latest entry {{ productLogStats.latestAt }}

Price & Margin Timeline

Processed Domain WH Price Str. S D
{{ row.processed_at || row.recorded_at || '--' }}
{{ row.domain || productLogs.meta.domain || '--' }}
Margin: {{ formatPercent(row.margin_percentage || row.margin) }}
{{ row.warehouse_id || row.wh || row.pricelist || '--' }}
Buy: {{ formatCurrency(row.buy_price_without_vat || row.buy_without_vat || row.buy_price_with_vat || row.buy, productLogs.meta.currency) }}
Sell: {{ formatCurrency(row.final_price_with_vat || row.price_with_vat || row.price, productLogs.meta.currency) }}
{{ row.strategy_used || row.strategy || '--' }} check_circle schedule verified pending

article Execution Log

hourglass_empty Generating AI explanation...
psychology AI Explanation:
{{ getShortTermExecutionLog() }}
Processed Domain WH Price Str.
{{ row.datum || row.processed_at || '--' }}
{{ row.projekt || row.domain || productLogs.meta.domain || '--' }}
Margin: {{ formatPercent(row.margin || row.margin_percentage) }}
{{ row.wh || row.warehouse || '--' }}
Buy: {{ formatCurrency(row.buy || row.cbuy || row.buy_price_without_vat, productLogs.meta.currency) }}
Sell: {{ formatCurrency(row.price || row.cprice || row.final_price_with_vat, productLogs.meta.currency) }}
{{ row.strategy || row.strategy_used || '--' }}
Showing {{ displayRows.length }} BigQuery rows (limit {{ productLogs.meta.limit }}).
info No log records found for the current selection.

Debug details

{{ productLogDiagnostics.summary }}

{{ productLogDiagnostics.json }}

psychology Log Entry Explanation

Date: {{ productLogExplanation.currentRow.processed_at || productLogExplanation.currentRow.datum || '--' }} Product: {{ productLogs.productId }} Domain: {{ productLogExplanation.currentRow.domain || productLogs.meta?.domain || '--' }}
Buy: {{ formatCurrency(productLogExplanation.currentRow.buy_price_without_vat || productLogExplanation.currentRow.buy || productLogExplanation.currentRow.cbuy, productLogs.meta?.currency || 'CZK') }} Sell: {{ formatCurrency(productLogExplanation.currentRow.final_price_with_vat || productLogExplanation.currentRow.price_with_vat || productLogExplanation.currentRow.price || productLogExplanation.currentRow.cprice, productLogs.meta?.currency || 'CZK') }} Margin: {{ formatPercent(productLogExplanation.currentRow.margin_percentage || productLogExplanation.currentRow.margin) }}

Full Execution Log:

{{ (productLogExplanation.currentRow.execution_log || []).join('\n') }}

Original Note:

edit_note Human Explanation:

Generating AI explanation...
info No explanation yet. Click Edit to add one or generate with AI.
error {{ productLogExplanation.error }}

Global Product Logs

View repricing changes for a product across every project, without selecting a domain.

link GET {{ globalProductLogEndpointPreview }}
error {{ globalProductLogs.error }}
Loading global product logs...
Entries: {{ globalProductLogs.meta.count }}
Product ID: {{ globalProductLogs.meta.product_id }}
Warehouse: {{ globalProductLogs.meta.warehouse }}
Limit: {{ globalProductLogs.meta.limit }}
Fetched: {{ globalProductLogs.meta.retrieved_at }}
Open endpoint
Recorded Project Warehouse Buy Price Sell Price Margin Strategy
{{ row.recorded_at || '--' }} {{ row.domain || '--' }} {{ row.warehouse || row.wh || '--' }} {{ formatCurrency(row.buy || row.buy_price_without_vat || row.buy_price_with_vat, (globalProductLogs.meta && globalProductLogs.meta.currency) || 'CZK') }} {{ formatCurrency(row.price_with_vat || row.price || row.price_without_vat, (globalProductLogs.meta && globalProductLogs.meta.currency) || 'CZK') }} {{ formatPercent(row.margin) }} {{ row.strategy || row.strategy_used || '--' }}
info No global log records found for this product.

Project Repricing Log

View all product repricing changes for a selected project, sorted by time (newest first).

filter_list Filters

Showing {{ projectLogFilteredRows.length }} of {{ projectLog.rows.length }} records
error {{ projectLog.error }}
Time Product ID Buy Price Price (VAT) Margin WH Note/EAN Actions
{{ formatDateTime(row.datum || row.processed_at || row.recorded_at) }} {{ row.dataid || row.product_id }} {{ (row.buy || row.buy_price_without_vat) ? formatPrice(row.buy || row.buy_price_without_vat) : '-' }} {{ row.price ? formatPrice(row.price) : (row.final_price_with_vat ? formatPrice(row.final_price_with_vat) : '-') }} {{ (row.margin ?? row.margin_percentage).toFixed(1) }}% - {{ row.wh || row.warehouse || '-' }} {{ row.pozn || row.note || '-' }} assignment bug_report
Showing first 200 of {{ projectLogFilteredRows.length }} filtered records. Use filters to narrow down results.
info No repricing logs found for this project in the last {{ projectLog.maxAge }} days.

Enabled Products

Products assigned to domains in product_website table - these can be repriced

analytics Products per Domain (filtered: {{ enabledProducts.domain }})

Domain Total Products Actions
{{ stat.domain }} {{ (stat.total || 0).toLocaleString() }} visibility
Total ({{ enabledProducts.statsSummary?.domains_count || 0 }} domains) {{ (enabledProducts.statsSummary?.total_products || 0).toLocaleString() }}

Showing {{ enabledProducts.rows.length }} of {{ enabledProducts.total }} products. More results available with specific filters.

error {{ enabledProducts.error }}
ID Product ID {{ enabledProducts.sortDir === 'asc' ? 'arrow_upward' : 'arrow_downward' }} Product Name {{ enabledProducts.sortDir === 'asc' ? 'arrow_upward' : 'arrow_downward' }} Domain Stock {{ enabledProducts.sortDir === 'asc' ? 'arrow_upward' : 'arrow_downward' }} Added {{ enabledProducts.sortDir === 'asc' ? 'arrow_upward' : 'arrow_downward' }} Actions
{{ row.id }} {{ row.product }} {{ row.product_name || '-' }} {{ row.domain }} {{ row.quantity || 0 }} {{ formatDateTime(row.datum) }} bug_report assignment
inbox

No products found for the selected criteria.

Repricing Debug Console

Inspect cached inputs, evaluate strategy coverage, and run wait-mode repricing in one place.

error {{ repricingDebug.strategyError }}
error {{ repricingDebug.submitError }}
history Product Log

{{ repricingDebug.repriceResult.data_used.basic_data?.nazev_produktu || 'Product details' }} open_in_new

Sell (excl. VAT) {{ repricingDebugSummary.previousPriceWithoutVatFormatted }} {{ repricingDebugSummary.sellWithoutVatFormatted }} Sell (incl. VAT) {{ repricingDebugSummary.previousPriceWithVatFormatted }} {{ repricingDebugSummary.sellWithVatFormatted }} Margin {{ repricingDebugSummary.marginFormatted }} Markup {{ repricingDebugSummary.markupFormatted }}
{{ tag.severity === 'warning' ? 'warning' : tag.severity === 'error' ? 'error' : tag.severity === 'success' ? 'check_circle' : 'label' }} {{ tag.tag }}
Product ID {{ repricingDebug.repriceResult.data_used.basic_data.id }} RegCIS {{ repricingDebug.repriceResult.data_used.basic_data.regcis }} open_in_new Brand {{ repricingDebug.repriceResult.data_used.basic_data.brand }} Price Buy {{ formatCurrency(repricingDebug.repriceResult.data_used.basic_data.price_buy, repricingDebug.repriceResult.final_price?.currency || 'CZK') }} Warehouse {{ repricingDebug.repriceResult.data_used.basic_data.warehouse }} Warehouse Cnt {{ repricingDebug.repriceResult.data_used.basic_data.warehouse_cnt }} Sell (excl. VAT) {{ repricingDebugSummary.sellWithoutVatFormatted }} VAT {{ repricingDebugSummary.vatPercentFormatted }} Orders (30d) {{ repricingDebug.repriceResult.data_used.basic_data.orders_last_month }}

storefront Competition Prices ({{ repricingDebug.repriceResult.data_used.competition_prices.length }})

{{ comp.domain.toUpperCase() }} ↗ {{ formatCurrency(comp.price || (comp.price_with_vat / 1.2), repricingDebug.repriceResult.final_price?.currency || 'EUR') }} ({{ formatCurrency(comp.price_with_vat || (comp.price * 1.2), repricingDebug.repriceResult.final_price?.currency || 'EUR') }} incl.)

Strategy Overview

  • {{ strategy.applies === true ? 'play_circle' : 'pause_circle' }} {{ strategy.name }}
    {{ strategy.active ? 'Active' : 'Inactive' }} {{ strategy.rules_matched }}/{{ strategy.rules_total }} rules match

Rules for {{ selectedRepricingStrategy.name }}

# Name Status Filter result Expression
{{ rule.priority || '-' }} {{ rule.name }} {{ rule.active ? 'Active' : 'Inactive' }} {{ rule.matched ? 'Matched' : 'Not matched' }} {{ rule.dsl_expression || '-' }}

DSL Variables Available

{{ Object.keys(filteredDslVariables.used).length + Object.keys(filteredDslVariables.unused).length }} / {{ Object.keys(repricingDebug.repriceResult.dsl_variables_available).length }} variables {{ Object.keys(filteredDslVariables.used).length }} used / {{ Object.keys(filteredDslVariables.unused).length }} unused
search
search_off No variables match "{{ repricingDebug.dslVariablesFilter }}"

Repricing Output

error {{ repricingDebug.repriceError }}
psychology
🤖 AI Price Explanation
terminal

Detailed Execution Log

Step-by-step repricing process

{{ logLine }}
Structured JSON (data_used)
{{ JSON.stringify(repricingDebug.repriceResult.data_used, null, 2) }}
info No repricing output captured.

Last run: {{ repricingDebug.lastRunAt }}

science

Reprice Simulate API

Test repricing with custom JSON data without using database. Send your own product data and get full execution log.

api API Docs
group

User Management

{{ (userManagement?.users || []).length }} user{{ (userManagement?.users || []).length !== 1 ? 's' : '' }} registered

search
error {{ userManagement.error }}

Loading users...

people_outline

No users found. Click Reload to refresh.

User Login Email Role Status Security Last Login Actions
{{ (user?.name || user?.login || '?').charAt(0).toUpperCase() }}
{{ user?.name || 'Unnamed' }}
link {{ user.redirect_url.length > 25 ? user.redirect_url.substring(0, 25) + '...' : user.redirect_url }}
{{ user?.login || 'N/A' }} {{ user.email }} verified Not set {{ user?.role === 'superadmin' ? 'admin_panel_settings' : 'person' }} {{ user?.role === 'superadmin' ? 'Super Admin' : 'User' }} {{ user?.is_active ? 'check_circle' : 'cancel' }} {{ user?.is_active ? 'Active' : 'Inactive' }}
vpn_lock IP security 2FA
{{ formatDateTime(user.last_login_at) }}
language {{ user.last_login_ip }}
Never
shield

Security Recommendations

security
Two-Factor Authentication (2FA)

Add TOTP or SMS verification for enhanced login security.

mark_email_read
Email Verification

Require users to verify their email before first login.

devices
Session Management

View and revoke active sessions from other devices.

history
Login History & Audit Log

Track all login attempts with IP, device, and timestamps.

API Keys Management

Manage API keys for accessing remote data endpoints (PLA feeds, external data, etc.)

ID Name API Key Domain Endpoint Prefix Last Used Created Actions
{{ key.id }} {{ key.key_name }} {{ key.api_key_display }} {{ key.domain }} All domains {{ key.endpoint_prefix }} All endpoints {{ formatDateTime(key.last_used_at) }} Never {{ formatDateTime(key.created_at) }}
No API keys found. Click "Create API Key" to add one.

Data Export/Import

Backup, restore, and migrate pricing management data between projects.

cloud_download

Export Data

Download configuration as JSON

Export projects, strategies, rules, guardrails, and other pricing configuration to a JSON file for backup or migration.

launch Open Export Tool
cloud_upload

Import Data

Restore from JSON backup

Import pricing configuration from a JSON export file. Supports merge (add/update) and replace (full overwrite) modes.

upload_file Open Import Tool
content_copy

Clone Project

Duplicate existing project

Create a new project by copying all settings, strategies, rules, and margin levels from an existing project.

copy_all Open Clone Tool
fact_check

Installation Health

Check system status

View installation health, missing data warnings, and REST API tutorials for loading products, competition data, and RRP.

health_and_safety Open Health Check

System Diagnostics

Comprehensive system health check including API keys, localStorage, database, endpoints, and deployment status.

Running diagnostics...
error {{ systemCheck.error }}
error
Problems Detected ({{ systemCheck.data.problems.length }})
  • {{ problem }}
lightbulb
Recommended Solutions ({{ systemCheck.data.solutions.length }})
  • {{ solution }}
{{ systemCheck.data.status === 'ok' ? 'check_circle' : systemCheck.data.status === 'warning' ? 'warning' : 'error' }}
Overall Status: {{ systemCheck.data.status.toUpperCase() }} Completed in {{ systemCheck.data.duration_ms }}ms at {{ new Date(systemCheck.data.completed_at).toLocaleString() }}
{{ systemCheck.output }}

No diagnostics have been executed yet. Click "Run Check" to start.

compare_arrows

Database Consistency Check

Compare table structures, row counts and data freshness between Testing and Production environments.

Last run: {{ dbConsistency.data.timestamp }} · {{ dbConsistency.data.total_issues }} issues
sync
Quick Database Sync Copy tables & data between Testing and Production environments
cloud_download Production → Testing
warning Testing → Production DANGEROUS
{{ maintenanceMode.enabled ? 'pause_circle' : 'engineering' }}
Maintenance Mode: {{ maintenanceMode.enabled ? 'ACTIVE' : 'OFF' }} {{ maintenanceMode.enabled ? 'All data processing is PAUSED — workers check /api/maintenance-status' : 'System is running normally. Enable to pause all data processing.' }}
Enabled: {{ maintenanceMode.info.enabled_at }}
By: {{ maintenanceMode.info.enabled_by }}
Reason: {{ maintenanceMode.info.reason || 'Manual maintenance' }}
Public API (no auth required): {{ currentBaseUrl }}/api/maintenance-status
Returns: {"can_process": true/false, "maintenance": bool, ...}
Workers should check: {{ currentBaseUrl }}/api/maintenance-status
monitor_heart
Database Process Monitor View active connections, detect write operations, kill blocking processes
Environment: {{ dbProcessMonitor.data.current_environment }} · Last updated: {{ dbProcessMonitor.data.timestamp }}
storage
{{ dbKey }} {{ dbData.host }} / {{ dbData.name }}
{{ dbData.write_attempt_count || 0 }} writes {{ dbData.process_count || 0 }} connections expand_more
warning Active Write Operations
ID User Host Type Time(s) Query Action
{{ proc.id }} {{ proc.user }} {{ proc.host }} {{ proc.write_type }} {{ proc.time }}s {{ proc.info }}
All Connections ({{ dbData.processes.length }})
ID User Host Command Time State Info Kill
No active connections
tips_and_updates Safe Maintenance Steps
  1. Stop all RabbitMQ workers/consumers
  2. Stop scheduled cron jobs (preparedata, reprice)
  3. Wait for active queries to complete (refresh above)
  4. Kill any remaining write processes
  5. Perform your maintenance / schema changes
  6. Restart workers and cron jobs
lock Table Locking (Persistent via Triggers)
{{ dbKey }} ({{ dbLocks.database }})
lock {{ lock.table_name }} {{ lock.locked_at }}
check_circle No tables locked
Click "Refresh" to see current table locks
How it works: Creates BEFORE INSERT/UPDATE/DELETE triggers that block all writes. Locks persist until manually unlocked. Reads are still allowed.
visibility Click "Refresh" to view active database connections and detect write operations
error {{ dbConsistency.error }}
terminal Live Console LIVE DONE
{{ log.time }} {{ log.message }} {{ log.message }} {{ log.message }} {{ log.message }} {{ log.message }}
Processing...
{{ dbConsistency.progress.currentDb }} » {{ dbConsistency.progress.currentTable }} {{ dbConsistency.progress.tablesChecked }} / {{ dbConsistency.progress.total }} tables
{{ dbConsistency.data.status === 'ok' ? 'verified' : dbConsistency.data.status === 'warning' ? 'warning' : 'gpp_bad' }}
{{ dbConsistency.data.status === 'ok' ? 'All Databases Consistent' : dbConsistency.data.status === 'warning' ? 'Minor Inconsistencies Found' : 'Critical Issues Detected' }} {{ dbConsistency.data.total_issues }} issue(s) across {{ dbConsistency.databases.length }} database(s) · {{ dbConsistency.data.timestamp }} · Active env: {{ dbConsistency.data.current_environment }}
sync {{ db.status === 'ok' ? 'check_circle' : db.status === 'warning' ? 'warning_amber' : 'cancel' }}
{{ db.key }}

{{ db.testing.purpose }}

{{ db.tables.length }} tables checked... {{ db.summary.total_tables }} tables expand_more
Testing
Not configured
Production
Not configured
info {{ issue }}
Table {{ dbConsistencySortIcon(db.key, 'name') }} Rows {{ dbConsistencySortIcon(db.key, 'row_diff_percent') }} Size Updated {{ dbConsistencySortIcon(db.key, 'time_diff_seconds') }} Status
{{ db.tables.length }} tables · {{ db.tables.filter(t => t.status !== 'ok').length }} issue(s) · Click to expand
compare_arrows

No Check Results Yet

Run a consistency check to compare table structures, row counts, and data freshness between your Testing and Production databases live.

{{ dbSync.selectedDirection === 'test_to_prod' ? 'gpp_bad' : 'sync' }}

{{ dbSync.selectedDirection === 'test_to_prod' ? '⚠️ Sync to PRODUCTION' : 'Sync to Testing' }}

{{ dbSync.selectedDirection === 'test_to_prod' ? 'This will overwrite LIVE production data!' : 'This will overwrite testing data.' }}

{{ dbSync.selectedDirection === 'test_to_prod' ? 'Source (Testing)' : 'Source (Production)' }}
{{ dbSync.selectedDb }}
arrow_forward
{{ dbSync.selectedDirection === 'test_to_prod' ? 'Target (PRODUCTION)' : 'Target (Testing)' }}
{{ dbSync.selectedDb }}
dns Connection Details
{{ dbSync.selectedDirection === 'test_to_prod' ? 'Source (Testing)' : 'Source (Production)' }}
Host: {{ dbSync.sourceInfo.host }}
DB: {{ dbSync.sourceInfo.name }}
User: {{ dbSync.sourceInfo.user }}
arrow_forward
{{ dbSync.selectedDirection === 'test_to_prod' ? 'Target (PRODUCTION)' : 'Target (Testing)' }}
Host: {{ dbSync.targetInfo.host }}
DB: {{ dbSync.targetInfo.name }}
User: {{ dbSync.targetInfo.user }}
sync Loading connection details...
bookmark Saved Templates
{{ dbSync.selectedTables.length }} / {{ dbSync.availableTablesData.length }} selected
info Run a consistency check first to populate the table list.
warning Please select at least one table to sync.

bookmark_add Save Sync Template

Save your current selection of {{ dbSync.selectedTables.length }} tables as a reusable template.

error

This action is irreversible. Live pricing, products, and all data will be overwritten. You will need to confirm again in the next step.

info

gpp_bad

FINAL CONFIRMATION

You are about to overwrite PRODUCTION data. This is irreversible.

database: {{ dbSync.selectedDb }}
table:    {{ dbSync.selectMode === 'multi' && dbSync.selectedTables.length > 0 ? dbSync.selectedTables.length + ' SELECTED TABLES' : 'ALL TABLES' }}
{{ dbSync.selectedTables.join(', ') }}
direction: Testing → Production
action:    {{ dbSync.selectMode === 'multi' && dbSync.selectedTables.length > 0 ? 'DROP ' + dbSync.selectedTables.length + ' TABLES + REPLACE' : 'DROP ALL + REPLACE' }}
recovery: NONE — NO BACKUP
sync {{ dbSync.result?.status === 'ok' ? 'check_circle' : 'error' }}
{{ dbSync.running ? 'Syncing...' : dbSync.result?.status === 'ok' ? 'Sync Complete!' : 'Sync Completed with Errors' }} {{ dbSync.initData ? dbSync.initData.db_key + ' — ' + dbSync.initData.direction : '' }}
{{ dbSync.result.tables_synced }}/{{ dbSync.result.total_tables }} tables {{ Number(dbSync.result.total_rows_copied).toLocaleString() }} rows · {{ dbSync.result.duration_sec }}s {{ Math.round(dbSync.result.total_rows_copied / dbSync.result.duration_sec).toLocaleString() }} rows/s avg
Source ({{ dbSync.initData.source_env }})
{{ dbSync.initData.source.name }}@{{ dbSync.initData.source.host }}
arrow_forward
Target ({{ dbSync.initData.target_env }})
{{ dbSync.initData.target.name }}@{{ dbSync.initData.target.host }}
{{ dbSync.progress.currentTable }} ({{ Number(dbSync.progress.currentTableRows).toLocaleString() }}/{{ Number(dbSync.progress.currentTableTotal).toLocaleString() }} rows) ⚡ {{ Number(dbSync.progress.tableSpeed).toLocaleString() }} rows/s {{ dbSync.progress.tablesDone }}/{{ dbSync.progress.total }} tables · {{ Number(dbSync.progress.rowsCopied).toLocaleString() }} rows total
schedule Elapsed: {{ Math.floor(dbSync.progress.elapsed / 60) }}:{{ String(dbSync.progress.elapsed % 60).padStart(2, '0') }} hourglass_bottom ETA table: {{ dbSync.progress.tableEta >= 60 ? Math.floor(dbSync.progress.tableEta / 60) + 'm ' + (dbSync.progress.tableEta % 60) + 's' : dbSync.progress.tableEta + 's' }} speed Speed: {{ Number(dbSync.progress.tableSpeed).toLocaleString() }} rows/s storage Copied: {{ Number(dbSync.progress.rowsCopied).toLocaleString() }}
terminal Sync Log LIVE
{{ log.time }} {{ log.message }} {{ log.message }} {{ log.message }}
Syncing...
error {{ dbSync.error }}
warning
{{ dbSync.result.errors.length }} Table(s) Failed to Sync

The sync completed, but these tables had errors. You can retry syncing just these tables.

error
{{ err.table }}

{{ err.error }}

info Successfully synced: {{ dbSync.result.tables_synced }} table(s)

Showing up to {{ state.rawMarketplacesMeta.limit }} rows. Additional rows are available via more specific filters.

Mark Data ID URL Price Currency Sale Price Shipping Actions
{{ row.datum }} {{ row.domain }} {{ row.src }} {{ row.mark }} {{ row.dataid }} {{ truncate(row.url, 80) }} N/A {{ displayNumber(row.price) }} {{ row.currency || 'N/A' }} {{ row.price_sale ? displayNumber(row.price_sale) : 'N/A' }} {{ displayNumber(row.shipping) }}
Showing {{ paginationBounds('rawMarketplaces').from }}-{{ paginationBounds('rawMarketplaces').to }} of {{ tableMeta.rawMarketplaces.total }}
Page {{ tableMeta.rawMarketplaces.page }} of {{ tableMeta.rawMarketplaces.pages }}

PLA Feeds Management

Manage PLA feeds data, XLSX imports, and processing.

Available Actions

  • Upload XLSX Files: Import new PLA feed data as binary files directly to database
  • Process Feeds: Parse uploaded XLSX files into structured product data
  • Download Files: Retrieve previously uploaded XLSX files from database
  • Re-import: Reprocess existing XLSX files with updated parsing logic

Recent PLA Feeds

Upload Date Domain Action File Name Status Actions
hourglass_empty

Loading PLA feeds...

error

Error loading feeds: {{ state.plaFeeds.error }}

folder_open

No PLA feeds uploaded yet. Use the "Upload New PLA Feed" button to get started.

{{ formatDate(feed.datum) }} {{ feed.domain || 'N/A' }} {{ feed.action || 'N/A' }} {{ feed.filename || 'N/A' }} {{ feed.processed === 'processed' ? 'Processed' : 'Pending' }}

Showing up to {{ state.rawDirectSitesMeta.limit }} rows. Additional rows are available via more specific filters.

Data ID URL Actions
{{ row.datum }} {{ row.domain }} {{ row.action }} {{ row.dataid }} {{ truncate(row.url, 100) }} N/A
Showing {{ paginationBounds('rawDirectSites').from }}-{{ paginationBounds('rawDirectSites').to }} of {{ tableMeta.rawDirectSites.total }}
Page {{ tableMeta.rawDirectSites.page }} of {{ tableMeta.rawDirectSites.pages }}

Showing up to {{ state.priceEanMeta.limit }} rows. Additional rows are available via more specific filters.

Domain Base Currency Instock Partner ID URL Actions
{{ row.datum }} {{ row.recrawled }} {{ row.domain }} {{ row.domainbase }} {{ row.tag }} {{ row.ean }} {{ formatCurrency(row.price, row.cur) }} {{ formatCurrency(row.price_full, row.cur) }} {{ row.confidence != null ? row.confidence : '' }} {{ row.cur }} {{ row.instock }} {{ row.partnerid }} {{ truncate(row.url, 100) }} N/A
Showing {{ paginationBounds('priceEan').from }}-{{ paginationBounds('priceEan').to }} of {{ tableMeta.priceEan.total }}
Page {{ tableMeta.priceEan.page }} of {{ tableMeta.priceEan.pages }}
Loading products cache data...

Showing up to {{ state.productsCache.meta.limit }} rows. Additional rows are available via more specific filters.

Data Size Actions
Loading cache data...
error
Error loading cache data

{{ state.productsCache.error }}

cached

No cached products found

{{ formatDateTime(row.cache_date) }}
{{ formatTimeAgo(row.cache_date) }}
{{ row.domain }}
{{ row.product_id }}
{{ row.product_name || 'N/A' }}
{{ formatCurrency(row.final_price, row.currency || 'CZK') }}
N/A
{{ formatBytes(row.data_size) }}
Showing {{ paginationBounds('productsCache').from }}-{{ paginationBounds('productsCache').to }} of {{ tableMeta.productsCache.total }}
Page {{ tableMeta.productsCache.page }} of {{ tableMeta.productsCache.pages }}

{{ getPageDescription(activeTab).title }}

{{ getPageDescription(activeTab).description }}

  • {{ feature }}
{{ globalLoadingStatus.primary }} {{ globalLoadingStatus.detail }}
sync error check_circle {{ loadingTracker.activeCount > 0 ? loadingTracker.completedCount + '/' + loadingTracker.totalCount : 'All loaded' }}
{{ item.name }}
{{ formatLoadingTime(item.elapsed) }} {{ item.duration }}ms