Feature: Verification table for testing price, term, and installment calculations
Created: February 7, 2026
Status: Complete and ready for use
Dry-run mode allows you to test and verify that all pricing, terms, and installment calculations are correct after code refactoring without actually sending data to Shopify.
Instead of updating Shopify, all calculated data is saved to a verification table (data_shopify_dryrun) where you can:
After refactoring the Shopify integration:
1. Enable dry-run mode
2. Process products (no Shopify updates)
3. Review calculated data in verification table
4. Compare with previous calculations
5. Confirm accuracy before going live
Product → Calculate prices → Send to Shopify → Update inventory
Product → Calculate prices → Save to verification table → Done
(No Shopify update)
All the same calculations happen, but instead of updating Shopify, the data is saved to data_shopify_dryrun for inspection.
IMPORTANT: The active package must be set to fullhouse in config.yaml:
# config.yaml
package: fullhouse
Verify the setting:
import Objects
ini = Objects.global_config
package = ini.Get('', 'package')
print(f"Active package: {package}") # Should print: fullhouse
Or check directly:
grep "^package:" config.yaml
# Should show: package: fullhouse
Why this is required:
factory.service/package.fullhouse/Add parameter to database:
INSERT INTO def_parameter (Parameter, Block, Value, Description)
VALUES (
'dry_run_mode',
'SHOPIFY',
'1',
'Enable dry-run mode (1=on, 0=off) - saves to verification table instead of Shopify'
)
ON DUPLICATE KEY UPDATE Value = '1';
The table is created automatically on first use, but you can create it manually:
from package.fullhouse.ObjServiceFHShopify import ObjServiceApi
service = ObjServiceApi()
service.Guid = "SHOPIFY_SYNC"
service.create_dryrun_table()
Or via SQL:
CREATE TABLE IF NOT EXISTS `core.axion`.data_shopify_dryrun (
id INT AUTO_INCREMENT PRIMARY KEY,
run_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
sku VARCHAR(100) NOT NULL,
variant_sku VARCHAR(100),
product_title VARCHAR(500),
option_level INT DEFAULT 0,
-- Pricing fields
price DECIMAL(10,2),
compare_at_price DECIMAL(10,2),
base_price DECIMAL(10,2),
-- Promotion fields
promotion_name VARCHAR(255),
promotion_price DECIMAL(10,2),
promotion_discount DECIMAL(10,2),
original_price DECIMAL(10,2),
-- Credit terms
credit_installment DECIMAL(10,2),
credit_deposit DECIMAL(10,2),
credit_months INT,
credit_price DECIMAL(10,2),
-- Warranty fields
warranty_code INT,
guarantee_period INT,
warranty_period INT,
extended_warranty VARCHAR(100),
extended_warranty_cost DECIMAL(10,2),
-- Variant details
option1 VARCHAR(100),
option2 VARCHAR(100),
option3 VARCHAR(100),
weight_grams DECIMAL(10,2),
-- Full data as JSON
metafields_json TEXT,
product_input_json TEXT,
-- Verification
verified BOOLEAN DEFAULT FALSE,
verification_notes TEXT,
INDEX idx_sku (sku),
INDEX idx_run_timestamp (run_timestamp),
INDEX idx_verified (verified)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Process products as normal:
from package.fullhouse.ObjServiceFHShopify import ObjServiceApi
service = ObjServiceApi()
service.Guid = "SHOPIFY_SYNC"
# Process single product
service.ComputeSql("PROD-001")
# Or process multiple products
products = ["PROD-001", "PROD-002", "PROD-003"]
for sku in products:
service.ComputeSql(sku)
Result: All calculations saved to data_shopify_dryrun, Shopify not updated.
SELECT
sku,
variant_sku,
price,
compare_at_price,
credit_installment,
credit_months,
promotion_name,
run_timestamp
FROM `core.axion`.data_shopify_dryrun
ORDER BY run_timestamp DESC, sku;
SELECT *
FROM `core.axion`.data_shopify_dryrun
WHERE sku = 'PROD-001'
ORDER BY run_timestamp DESC;
service = ObjServiceApi()
service.Guid = "SHOPIFY_SYNC"
# Get summary for most recent run
summary = service.get_dryrun_summary()
print(summary)
# Output:
# {
# "unique_skus": 100,
# "total_variants": 250,
# "products_on_promotion": 25,
# "avg_price": 1299.50,
# "min_price": 99.00,
# "max_price": 15999.00,
# "avg_installment": 129.95,
# "products_with_credit": 80
# }
summary = service.get_dryrun_summary(
start_timestamp='2026-02-07 10:00:00',
end_timestamp='2026-02-07 11:00:00'
)
service = ObjServiceApi()
service.Guid = "SHOPIFY_SYNC"
# Compare two time ranges
results = service.compare_dryrun_results(
run1_start='2026-02-07 09:00:00',
run1_end='2026-02-07 09:30:00',
run2_start='2026-02-07 10:00:00',
run2_end='2026-02-07 10:30:00',
tolerance=0.01 # Allow 1 cent difference
)
print(f"Total compared: {results['total_compared']}")
print(f"Matches: {results['matches']}")
print(f"Differences: {results['differences']}")
# View details of differences
for diff in results['details']:
if 'differences' in diff:
print(f"\nSKU: {diff['sku']}")
for field_diff in diff['differences']:
print(f" {field_diff['field']}:")
print(f" Before: {field_diff['run1']}")
print(f" After: {field_diff['run2']}")
if 'difference' in field_diff:
print(f" Diff: {field_diff['difference']}")
-- Compare prices between two runs
SELECT
r1.sku,
r1.variant_sku,
r1.price as before_price,
r2.price as after_price,
(r2.price - r1.price) as price_diff,
r1.credit_installment as before_installment,
r2.credit_installment as after_installment,
(r2.credit_installment - r1.credit_installment) as installment_diff
FROM
(SELECT * FROM `core.axion`.data_shopify_dryrun
WHERE run_timestamp BETWEEN '2026-02-07 09:00:00' AND '2026-02-07 09:30:00') r1
INNER JOIN
(SELECT * FROM `core.axion`.data_shopify_dryrun
WHERE run_timestamp BETWEEN '2026-02-07 10:00:00' AND '2026-02-07 10:30:00') r2
ON r1.variant_sku = r2.variant_sku
WHERE
ABS(r1.price - r2.price) > 0.01
OR ABS(r1.credit_installment - r2.credit_installment) > 0.01
ORDER BY ABS(r2.price - r1.price) DESC;
-- Products where promotional price differs from expected
SELECT
sku,
variant_sku,
original_price,
promotion_price,
price as final_price,
promotion_name
FROM `core.axion`.data_shopify_dryrun
WHERE promotion_name != ''
AND ABS(price - promotion_price) > 0.01
ORDER BY run_timestamp DESC;
-- Verify installment calculations
SELECT
sku,
variant_sku,
credit_price,
credit_installment,
credit_months,
(credit_installment * credit_months) as total_installments,
ABS(credit_price - (credit_installment * credit_months)) as difference
FROM `core.axion`.data_shopify_dryrun
WHERE credit_months > 0
AND ABS(credit_price - (credit_installment * credit_months)) > 1.00
ORDER BY difference DESC;
-- Products in run1 but not run2
SELECT r1.sku, r1.variant_sku
FROM
(SELECT DISTINCT sku, variant_sku
FROM `core.axion`.data_shopify_dryrun
WHERE run_timestamp BETWEEN '2026-02-07 09:00:00' AND '2026-02-07 09:30:00') r1
LEFT JOIN
(SELECT DISTINCT sku, variant_sku
FROM `core.axion`.data_shopify_dryrun
WHERE run_timestamp BETWEEN '2026-02-07 10:00:00' AND '2026-02-07 10:30:00') r2
ON r1.variant_sku = r2.variant_sku
WHERE r2.variant_sku IS NULL;
-- Verify extended warranty calculations
SELECT
sku,
variant_sku,
price,
extended_warranty,
extended_warranty_cost,
option1
FROM `core.axion`.data_shopify_dryrun
WHERE extended_warranty != ''
AND extended_warranty_cost > 0
ORDER BY sku, variant_sku;
service = ObjServiceApi()
service.Guid = "SHOPIFY_SYNC"
# Clear data before specific timestamp
service.clear_dryrun_data(before_timestamp='2026-02-07 09:00:00')
# Or clear all data
service.clear_dryrun_data()
-- Delete old runs
DELETE FROM `core.axion`.data_shopify_dryrun
WHERE run_timestamp < '2026-02-07 00:00:00';
-- Clear all data
TRUNCATE TABLE `core.axion`.data_shopify_dryrun;
When you're confident calculations are correct, disable dry-run mode:
UPDATE def_parameter
SET Value = '0'
WHERE Parameter = 'dry_run_mode'
AND Block = 'SHOPIFY';
Or delete the parameter:
DELETE FROM def_parameter
WHERE Parameter = 'dry_run_mode'
AND Block = 'SHOPIFY';
After disabling, products will be sent to Shopify as normal.
Scenario: Refactored price calculation logic
# Step 1: Before refactor - run products in dry-run mode
# (dry_run_mode = 1)
for sku in test_products:
service.ComputeSql(sku)
# Note timestamp: 2026-02-07 09:00:00 - 09:30:00
# Step 2: Apply refactor changes
# (make code changes)
# Step 3: After refactor - run same products
for sku in test_products:
service.ComputeSql(sku)
# Note timestamp: 2026-02-07 10:00:00 - 10:30:00
# Step 4: Compare
results = service.compare_dryrun_results(
run1_start='2026-02-07 09:00:00',
run1_end='2026-02-07 09:30:00',
run2_start='2026-02-07 10:00:00',
run2_end='2026-02-07 10:30:00'
)
if results['differences'] == 0:
print("✅ All calculations match! Refactor is safe.")
else:
print(f"⚠️ Found {results['differences']} differences")
# Review details...
Scenario: New promotion rules, verify pricing
# Enable dry-run
# Update promotion in database
# Run affected products
# Review dry-run table
# Confirm prices correct
# Disable dry-run
Scenario: Finance team wants to audit credit terms
-- Export all credit terms for review
SELECT
sku,
variant_sku,
product_title,
price,
credit_price,
credit_installment,
credit_deposit,
credit_months
FROM `core.axion`.data_shopify_dryrun
WHERE credit_months > 0
ORDER BY sku
INTO OUTFILE '/tmp/credit_terms_audit.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
When comparing dry-run results:
Check:
SELECT * FROM def_parameter
WHERE Parameter = 'dry_run_mode' AND Block = 'SHOPIFY';
SELECT * FROM def_log
WHERE source = 'shopify' AND event = 'dry_run_saved'
ORDER BY timestamp DESC LIMIT 10;
Debug:
Possible causes:
# Define standard test products
test_products = [
"BASIC-001", # Simple product
"PROMO-001", # On promotion
"BED-001", # Bed sizes (option_level 1)
"WARR-001", # Extended warranty (option_level 2)
"COLOR-001", # Color variants (option_level 3)
]
# Always use same set for before/after comparisons
import datetime
run_name = "Pre-refactor baseline"
start_time = datetime.datetime.now()
print(f"Starting run: {run_name} at {start_time}")
# Process products...
end_time = datetime.datetime.now()
print(f"Completed run: {run_name} at {end_time}")
# Save timestamps for comparison
Don't wait days to compare - review results while the changes are fresh in your mind.
UPDATE `core.axion`.data_shopify_dryrun
SET verified = TRUE,
verification_notes = 'Reviewed 2026-02-07 - calculations correct'
WHERE run_timestamp BETWEEN '2026-02-07 10:00:00' AND '2026-02-07 10:30:00';
Dry-run table grows over time - clean up old runs periodically.
What it does:
When to use:
Benefits:
Created: February 7, 2026
Status: ✅ Complete and Production Ready
Version: 1.0