{{ getPageDescription(activeTab).title }}
{{ getPageDescription(activeTab).description }}
- {{ feature }}
Configuration Overview
Active projects with their strategies and rules.
| Details | Actions | |
|---|---|---|
|
{{ row.label }}
{{ row.subtitle }}
|
{{ row.meta }} -- |
|
| Order | {{ t('tableSort.projects.domain') }} | {{ t('tableSort.projects.strategies') }} | {{ t('tableSort.projects.warehouse') }} | {{ t('tableSort.projects.status') }} | Modified | Actions | |
|---|---|---|---|---|---|---|---|
|
{{ project.domain }}
{{ project.slug || 'N/A' }}
|
{{ strategy.name }}
None
|
{{ project.warehouses_mask || 'N/A' }} | {{ project.is_active ? 'Active' : 'Inactive' }} |
{{ project.modified_by }}
-
{{ project.modified_at ? formatDateTime(project.modified_at) : '' }} |
| 🎨 | 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) : '' }} |
| 🎨 | 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) : '' }} |
No guardrails found
Click "New Guardrail" to create your first reusable pricing constraint| 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) : '' }} |
| 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 }} | Yes ? | {{ rule.statusLabel }} | {{ rule.ownerName || '?' }} |
|
| No rules found. | |||||||||
| Actions | ||||
|---|---|---|---|---|
| {{ tier.tier_name }} | {{ tier.tier_remote_id }} | {{ tier.project_domain || 'Shared' }} | {{ tier.description }} |
|
| ID | Entity Type | Entity Name | Action | User | Date & Time | Actions |
|---|---|---|---|---|---|---|
| {{ log.id }} | {{ log.entity_type === 'single_rule' ? 'single rule' : log.entity_type }} | {{ log.action_type }} | {{ log.user_name || 'Unknown' }} | {{ formatDateTime(log.created_at) }} |
|
|
| No log entries found. | ||||||
| ID | API Key | Endpoint | Method | Status | IP Address | User Agent | Date & Time |
|---|---|---|---|---|---|---|---|
| {{ log.id }} | {{ log.api_key_prefix || 'N/A' }} | {{ log.method }} | {{ log.status_code }} | {{ log.ip_address }} | {{ formatDateTime(log.created_at) }} | ||
| No API key usage logs found. | |||||||
📈 Sales Analytics
View sales statistics, turnovers, margins and missed revenue opportunities.
🏆 Top Products
View best selling products by quantity, revenue or order count.
No products found for the selected period.
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) }} |
= sum of (lowestPrice − ourPrice)
= €{{ formatNumber(salesAnalytics.missedRevenue.summary?.last_31_days?.total_price_diff || 0, 0) }} price diff × 25%
| Time | Order | Product | Domain | Our Price | Lowest Comp. | Median | Diff (Lowest) | Missed € | Compare |
|---|---|---|---|---|---|---|---|---|---|
| {{ formatDateTime(item.datum) }} | {{ item.ordernumber || item.oid }} | {{ item.product }} | {{ item.domain }} | {{ item.currency === 'EUR' ? '€' : item.currency }}{{ formatNumber(item.sale_price, 2) }} | {{ item.currency === 'EUR' ? '€' : item.currency }}{{ formatNumber(item.lowest_competitor, 2) }} - | {{ item.currency === 'EUR' ? '€' : item.currency }}{{ formatNumber(item.median_competitor, 2) }} - | {{ item.diff_to_lowest > 0 ? '+' : '' }}{{ formatNumber(item.diff_to_lowest, 2) }} - | €{{ formatNumber(item.missed_revenue, 2) }} ✓ Best - | 🔗 Compare {{ item.competitor_count }} comps - |
| 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) }}
| 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. | ||||||||
| 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. | ||||||||
| # | Product ID | Product Name | Domain | Qty | Orders | Revenue € | Avg Price € | Margin € | Margin % | Last Sale | Actions |
|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ index + 1 }} | {{ product.product_id }} | {{ 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. | |||||||||||
Order Detail: {{ salesAnalytics.orderDetail?.order?.oid }}
Order Items
| Product ID | RegCIS | Qty | Price/unit | Total | Buy Price | Margin |
|---|---|---|---|---|---|---|
| {{ item.product }} | {{ item.regcis }} | {{ item.ks }} | €{{ formatNumber(item.priceks, 2) }} | €{{ formatNumber(item.price, 2) }} | €{{ formatNumber(item.buy_price, 2) }} | €{{ formatNumber(item.margin, 2) }} ({{ item.margin_percent }}%) |
| TOTALS | €{{ formatNumber(salesAnalytics.orderDetail.summary?.total_revenue, 2) }} | €{{ formatNumber(salesAnalytics.orderDetail.summary?.total_cost, 2) }} | €{{ formatNumber(salesAnalytics.orderDetail.summary?.total_margin, 2) }} ({{ salesAnalytics.orderDetail.summary?.margin_percent }}%) | |||
| 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) }} |
|
| Actions | |||||
|---|---|---|---|---|---|
| {{ route.name }} | {{ route.route || 'N/A' }} | {{ route.description || 'N/A' }} | {{ route.endpoint_url || 'N/A' }} | {{ route.is_active ? 'Active' : 'Inactive' }} |
|
No dynamic tags found
Click "New Dynamic Tag" to create a tag that will be assigned to products matching a filter expression| 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) : '' }} |
| 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' }} | {{ 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:
Click to select XLSX file or drag & drop
Supported formats: .xlsx, .xlsStep 2: Map Columns to Database Fields
Import Preview - First 10 Products
| Product Name | EAN/ID | Domain | Price | Type | Tag | Action |
|---|---|---|---|---|---|---|
|
{{ row.name || '-' }}
? Has validation errors
|
{{ row.product_id || '-' }} |
{{ row.domena || '-' }} | {{ row.price || '-' }} {{ row.currency }} | {{ 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 }}
|
RRP Prices Detected - Create Rules?
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.
Price & Margin Timeline
| Processed | Domain | Warehouse | Buy (w/o VAT) | Sell (with VAT) | Margin % | Strategy | Competition | Note | Debug | Explain |
|---|---|---|---|---|---|---|---|---|---|---|
| {{ row.processed_at || row.recorded_at || '--' }} | {{ row.domain || productLogs.meta.domain || '--' }} | {{ row.warehouse_id || row.wh || row.pricelist || '--' }} | {{ formatCurrency(row.buy_price_without_vat || row.buy_without_vat || row.buy_price_with_vat || row.buy, productLogs.meta.currency) }} | {{ formatCurrency(row.final_price_with_vat || row.price_with_vat || row.price, productLogs.meta.currency) }} | {{ formatPercent(row.margin_percentage || row.margin) }} | {{ row.strategy_used || row.strategy || '--' }} | {{ row.competition_count !== undefined && row.competition_count !== null ? row.competition_count : '--' }} |
| Date | Buy (CZK) | Sell (CZK) | Margin % | Warehouse | Note | Explain |
|---|---|---|---|---|---|---|
| {{ row.datum || '--' }} | {{ formatCurrency(row.cbuy || row.buy, 'CZK') }} | {{ formatCurrency(row.cprice || row.price, 'CZK') }} | {{ formatPercent(row.margin) }} | {{ row.wh || '--' }} |
Debug details
{{ productLogDiagnostics.summary }}
{{ productLogDiagnostics.json }}
Global Product Logs
View repricing changes for a product across every project, without selecting a domain.
| 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 || '--' }} |
Project Repricing Log
View all product repricing changes for a selected project, sorted by time (newest first).
Filters
Enabled Products
Products assigned to domains in product_website table - these can be repriced
Products per Domain (filtered: {{ enabledProducts.domain }})
| Domain | Total Products | Actions |
|---|---|---|
| {{ stat.domain }} | {{ (stat.total || 0).toLocaleString() }} | |
| 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.
| ID | Product ID | Product Name | Domain | Stock | Added | Actions |
|---|---|---|---|---|---|---|
| {{ row.id }} | {{ row.product }} | {{ row.domain }} | {{ row.quantity || 0 }} | {{ formatDateTime(row.datum) }} |
No products found for the selected criteria.
Repricing Debug Console
Inspect cached inputs, evaluate strategy coverage, and run wait-mode repricing in one place.
Strategy Overview
-
{{ strategy.name }}
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{{ key }}
{{ formatDslVariableValue(getDslVarValue(varData)) }}
{{ getDslVarVat(varData) === 'excl' ? 'excl VAT' : 'incl VAT' }}
{{ key }}
{{ formatDslVariableValue(getDslVarValue(varData)) }}
Repricing Output
🤖 AI Price Explanation
Detailed Execution Log
Step-by-step repricing process
{{ JSON.stringify(repricingDebug.repriceResult.data_used, null, 2) }}
Reprice Simulate API
Test repricing with custom JSON data without using database. Send your own product data and get full execution log.