- Implement test script for Bygma ESG data import (test_bygma_import.js) - Create test script for Bygma Prisbog import functionality (test_bygma_prisbog_import.js) - Develop full integration test for Bygma materials system (test_full_bygma_integration.js) - Add materials synchronization test script (test_materials_sync.js) - Ensure database connection and import statistics are logged - Validate API visibility and price updates in integration tests - Include sample data checks and cleanup procedures in tests
283 lines
8.5 KiB
SQL
283 lines
8.5 KiB
SQL
-- Bygma Prisbog Database Schema - Final Version
|
|
-- Perfectly aligned with actual CSV structure
|
|
-- CSV columns: Varegrp;VareNr;Tekst;Enhed;BruttoPris;Opdat;Nettopris;DB-nr;EAN-nr;Std
|
|
|
|
USE tilbudgivern;
|
|
|
|
-- Drop existing triggers first to avoid conflicts
|
|
DROP TRIGGER IF EXISTS tr_bygma_products_update_last_seen;
|
|
DROP TRIGGER IF EXISTS tr_bygma_price_history_calculate_change;
|
|
|
|
-- Drop existing tables in correct order to avoid foreign key constraints
|
|
DROP TABLE IF EXISTS bygma_materials_mapping;
|
|
DROP TABLE IF EXISTS bygma_price_history;
|
|
DROP TABLE IF EXISTS bygma_import_log;
|
|
DROP TABLE IF EXISTS bygma_product_groups;
|
|
DROP TABLE IF EXISTS bygma_products;
|
|
|
|
-- Main table for Bygma products - exactly matching CSV structure
|
|
CREATE TABLE bygma_products (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
|
|
-- Exact CSV columns
|
|
varegrp VARCHAR(10), -- CSV: Varegrp
|
|
vareNr VARCHAR(50) NOT NULL, -- CSV: VareNr (unique identifier)
|
|
tekst TEXT, -- CSV: Tekst
|
|
enhed VARCHAR(20), -- CSV: Enhed
|
|
db_nr VARCHAR(50), -- CSV: DB-nr
|
|
ean_nr VARCHAR(50), -- CSV: EAN-nr
|
|
std VARCHAR(50), -- CSV: Std
|
|
|
|
-- Current pricing (from most recent import)
|
|
current_brutto_pris DECIMAL(15,2), -- CSV: BruttoPris
|
|
current_netto_pris DECIMAL(15,2), -- CSV: Nettopris
|
|
current_opdat TINYINT, -- CSV: Opdat
|
|
|
|
-- Metadata
|
|
first_seen DATE,
|
|
last_seen DATE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
-- Indexes for performance
|
|
UNIQUE KEY idx_vareNr (vareNr),
|
|
INDEX idx_varegrp (varegrp),
|
|
INDEX idx_tekst (tekst(100)),
|
|
INDEX idx_current_prices (current_brutto_pris, current_netto_pris),
|
|
INDEX idx_active (is_active),
|
|
INDEX idx_last_seen (last_seen)
|
|
);
|
|
|
|
-- Product groups for categorization
|
|
CREATE TABLE bygma_product_groups (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
varegrp VARCHAR(10) NOT NULL UNIQUE,
|
|
group_name VARCHAR(255),
|
|
description TEXT,
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
INDEX idx_varegrp (varegrp)
|
|
);
|
|
|
|
-- Price history to track all price changes over time
|
|
CREATE TABLE bygma_price_history (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
product_id INT NOT NULL,
|
|
|
|
-- Exact price fields from CSV
|
|
brutto_pris DECIMAL(15,2), -- CSV: BruttoPris
|
|
netto_pris DECIMAL(15,2), -- CSV: Nettopris
|
|
opdat TINYINT, -- CSV: Opdat
|
|
|
|
-- Price change tracking
|
|
price_change_pct DECIMAL(8,4) DEFAULT 0,
|
|
is_current BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Import metadata
|
|
import_date DATE,
|
|
import_file VARCHAR(500),
|
|
import_batch_id VARCHAR(100),
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
FOREIGN KEY (product_id) REFERENCES bygma_products(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_product (product_id),
|
|
INDEX idx_import_batch (import_batch_id),
|
|
INDEX idx_import_date (import_date),
|
|
INDEX idx_current (is_current),
|
|
INDEX idx_prices (brutto_pris, netto_pris)
|
|
);
|
|
|
|
-- Import log to track all imports
|
|
CREATE TABLE bygma_import_log (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
import_batch_id VARCHAR(100) NOT NULL UNIQUE,
|
|
filename VARCHAR(500),
|
|
filepath VARCHAR(500),
|
|
file_hash VARCHAR(64),
|
|
file_size BIGINT,
|
|
|
|
-- Import statistics
|
|
total_rows INT DEFAULT 0,
|
|
processed_rows INT DEFAULT 0,
|
|
successful_rows INT DEFAULT 0,
|
|
failed_rows INT DEFAULT 0,
|
|
new_products INT DEFAULT 0,
|
|
updated_products INT DEFAULT 0,
|
|
updated_prices INT DEFAULT 0,
|
|
|
|
-- Status tracking
|
|
status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
|
|
error_summary TEXT,
|
|
warnings TEXT,
|
|
|
|
-- Timing
|
|
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
completed_at TIMESTAMP NULL,
|
|
duration_seconds INT,
|
|
|
|
-- User tracking
|
|
imported_by VARCHAR(100),
|
|
|
|
INDEX idx_batch_id (import_batch_id),
|
|
INDEX idx_status (status),
|
|
INDEX idx_started_at (started_at),
|
|
INDEX idx_filename (filename(100))
|
|
);
|
|
|
|
-- Mapping table to connect Bygma products with existing materials
|
|
CREATE TABLE bygma_materials_mapping (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
bygma_product_id INT NOT NULL,
|
|
material_id INT, -- Can be NULL if not mapped yet
|
|
|
|
-- Mapping metadata
|
|
mapping_type ENUM('auto', 'manual', 'ai_suggested') DEFAULT 'auto',
|
|
mapping_confidence DECIMAL(5,4), -- 0.0 to 1.0
|
|
auto_update_price BOOLEAN DEFAULT FALSE,
|
|
price_markup_pct DECIMAL(8,4) DEFAULT 0, -- Markup percentage
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
mapped_by VARCHAR(100),
|
|
|
|
FOREIGN KEY (bygma_product_id) REFERENCES bygma_products(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_bygma_product (bygma_product_id),
|
|
INDEX idx_material (material_id),
|
|
INDEX idx_mapping_type (mapping_type),
|
|
INDEX idx_auto_update (auto_update_price),
|
|
|
|
-- Ensure only one mapping per Bygma product
|
|
UNIQUE KEY unique_bygma_mapping (bygma_product_id)
|
|
);
|
|
|
|
-- Simplified Triggers (without recursive calls)
|
|
|
|
DELIMITER //
|
|
|
|
-- Trigger: Update last_seen and current prices when new price comes in
|
|
CREATE TRIGGER tr_bygma_products_update_last_seen
|
|
AFTER INSERT ON bygma_price_history
|
|
FOR EACH ROW
|
|
BEGIN
|
|
UPDATE bygma_products
|
|
SET last_seen = NEW.import_date,
|
|
current_brutto_pris = NEW.brutto_pris,
|
|
current_netto_pris = NEW.netto_pris,
|
|
current_opdat = NEW.opdat,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = NEW.product_id;
|
|
END//
|
|
|
|
-- Trigger: Calculate price change percentage and mark previous prices as not current
|
|
CREATE TRIGGER tr_bygma_price_history_calculate_change
|
|
BEFORE INSERT ON bygma_price_history
|
|
FOR EACH ROW
|
|
BEGIN
|
|
DECLARE prev_price DECIMAL(15,2);
|
|
|
|
-- Find previous price
|
|
SELECT netto_pris INTO prev_price
|
|
FROM bygma_price_history
|
|
WHERE product_id = NEW.product_id
|
|
AND is_current = TRUE
|
|
ORDER BY created_at DESC
|
|
LIMIT 1;
|
|
|
|
-- Calculate change if there's a previous price
|
|
IF prev_price IS NOT NULL AND prev_price > 0 THEN
|
|
SET NEW.price_change_pct = ((NEW.netto_pris - prev_price) / prev_price) * 100;
|
|
END IF;
|
|
|
|
-- Mark all other prices for this product as not current
|
|
UPDATE bygma_price_history
|
|
SET is_current = FALSE
|
|
WHERE product_id = NEW.product_id;
|
|
END//
|
|
|
|
DELIMITER ;
|
|
|
|
-- Useful views for reporting and analysis
|
|
|
|
-- View: Current prices with product details
|
|
CREATE VIEW v_bygma_current_prices AS
|
|
SELECT
|
|
p.id,
|
|
p.vareNr,
|
|
p.tekst,
|
|
p.enhed,
|
|
p.varegrp,
|
|
p.current_brutto_pris,
|
|
p.current_netto_pris,
|
|
p.current_opdat,
|
|
p.last_seen,
|
|
pg.group_name,
|
|
mm.material_id as mapped_material_id,
|
|
mm.mapping_type,
|
|
mm.auto_update_price,
|
|
mm.price_markup_pct
|
|
FROM bygma_products p
|
|
LEFT JOIN bygma_product_groups pg ON p.varegrp = pg.varegrp
|
|
LEFT JOIN bygma_materials_mapping mm ON p.id = mm.bygma_product_id
|
|
WHERE p.is_active = TRUE;
|
|
|
|
-- View: Price history with changes
|
|
CREATE VIEW v_bygma_price_changes AS
|
|
SELECT
|
|
ph.id,
|
|
p.vareNr,
|
|
p.tekst,
|
|
ph.brutto_pris,
|
|
ph.netto_pris,
|
|
ph.opdat,
|
|
ph.price_change_pct,
|
|
ph.import_date,
|
|
ph.import_file,
|
|
ph.is_current
|
|
FROM bygma_price_history ph
|
|
JOIN bygma_products p ON ph.product_id = p.id
|
|
ORDER BY ph.import_date DESC, p.vareNr;
|
|
|
|
-- View: Import statistics
|
|
CREATE VIEW v_bygma_import_stats AS
|
|
SELECT
|
|
il.id,
|
|
il.import_batch_id,
|
|
il.filename,
|
|
il.total_rows,
|
|
il.processed_rows,
|
|
il.successful_rows,
|
|
il.failed_rows,
|
|
il.new_products,
|
|
il.updated_products,
|
|
il.updated_prices,
|
|
il.status,
|
|
il.started_at,
|
|
il.completed_at,
|
|
il.duration_seconds,
|
|
il.imported_by,
|
|
ROUND((il.successful_rows / NULLIF(il.total_rows, 0)) * 100, 2) as success_rate_pct
|
|
FROM bygma_import_log il
|
|
ORDER BY il.started_at DESC;
|
|
|
|
-- Add some basic product groups based on common Bygma categories
|
|
INSERT INTO bygma_product_groups (varegrp, group_name) VALUES
|
|
('1000', 'Tømmer og planker'),
|
|
('3310', 'Porebeton og isolering'),
|
|
('3320', 'Leca og letbeton'),
|
|
('3330', 'Mursten og blokke'),
|
|
('2000', 'Plader og isolering'),
|
|
('4000', 'Tagmaterialer'),
|
|
('5000', 'Vinduer og døre'),
|
|
('6000', 'Beton og cement'),
|
|
('7000', 'Værktøj'),
|
|
('8000', 'Beslag og fittings'),
|
|
('9000', 'Diverse byggematerialer')
|
|
ON DUPLICATE KEY UPDATE group_name = VALUES(group_name);
|