refactor: привести кодовую базу в соответствие с канонами bible
- 400 → 422 для всех ошибок валидации входных данных (handlers: export, quote, sync, vendor_spec, partnumber_books, pricelist) - SQL-запросы вынесены из handlers в localdb (partnumber_books, pricelist, support_bundle); ValidateMariaDBConnection перенесён в internal/db/validate.go - List-ответы унифицированы: ключ items, поля total_count/page/per_page/total_pages (component, pricelist, partnumber_books); шаблоны обновлены - Молчаливые ошибки заменены на slog.Warn/Error (support_bundle, vendor_spec, component, configuration, local_configuration, localdb) - N+1 запросы устранены: batch-запросы в export.go и vendor_workspace_import.go - fmt.Println → slog в cmd/ (qfs, migrate, migrate_ops_projects, migrate_project_updated_at) - Заголовки recovery/verify добавлены во все 28 SQL-миграций - Добавлены bible-local/runtime-flows.md и bible-local/decisions/ - Обновлён субмодуль bible до v0.2.0-13 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: lot
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if lot_category already exists
|
||||
-- recovery.partial: DROP INDEX IF EXISTS idx_lot_category ON lot; ALTER TABLE lot DROP COLUMN lot_category;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: lot_category column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='lot' AND column_name='lot_category' HAVING COUNT(*)=0
|
||||
|
||||
-- Migration: Add lot_category column to lot table
|
||||
-- Run this migration manually on the database
|
||||
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if custom_price already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN custom_price;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: custom_price column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='custom_price' HAVING COUNT(*)=0
|
||||
|
||||
-- Add custom_price column to qt_configurations table
|
||||
ALTER TABLE qt_configurations ADD COLUMN custom_price DECIMAL(12,2) NULL COMMENT 'User-defined custom total price';
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_lot_metadata
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if is_hidden already exists
|
||||
-- recovery.partial: ALTER TABLE qt_lot_metadata DROP COLUMN is_hidden;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: is_hidden column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_lot_metadata' AND column_name='is_hidden' HAVING COUNT(*)=0
|
||||
|
||||
-- Add is_hidden column to qt_lot_metadata table
|
||||
ALTER TABLE qt_lot_metadata ADD COLUMN is_hidden BOOLEAN DEFAULT FALSE COMMENT 'Hide component from configurator';
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if price_updated_at already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN price_updated_at;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: price_updated_at column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='price_updated_at' HAVING COUNT(*)=0
|
||||
|
||||
-- Add price_updated_at column to qt_configurations table
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN price_updated_at TIMESTAMP NULL DEFAULT NULL
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if owner_username already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN owner_username;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: owner_username column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='owner_username' HAVING COUNT(*)=0
|
||||
|
||||
-- Store configuration owner as username (instead of relying on numeric user_id)
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN owner_username VARCHAR(100) NOT NULL DEFAULT '' AFTER user_id,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: local_configuration_versions (SQLite), local_configurations (SQLite)
|
||||
-- recovery.not-started: safe to re-run only if table does not exist; fails if table or column already present
|
||||
-- recovery.partial: roll back: DROP TABLE IF EXISTS local_configuration_versions; run SQLite migration recovery
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: local_configuration_versions table missing | SELECT 1 FROM sqlite_master WHERE type='table' AND name='local_configuration_versions' HAVING COUNT(*)=0
|
||||
|
||||
-- Add full-snapshot versioning for local configurations (SQLite)
|
||||
-- 1) Create local_configuration_versions
|
||||
-- 2) Add current_version_id to local_configurations
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; uses INFORMATION_SCHEMA check before DROP FOREIGN KEY
|
||||
-- recovery.partial: no rollback needed; FK was dropped intentionally
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: user_id column is still NOT NULL | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='user_id' AND is_nullable='NO' HAVING COUNT(*)>0
|
||||
|
||||
-- Detach qt_configurations from qt_users (ownership is owner_username text)
|
||||
-- Safe for MySQL 8+/MariaDB 10.2+ via INFORMATION_SCHEMA checks.
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if app_version already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN app_version;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: app_version column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='app_version' HAVING COUNT(*)=0
|
||||
|
||||
-- Track application version used for configuration writes (create/update via sync)
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN app_version VARCHAR(64) NULL DEFAULT NULL AFTER owner_username;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_projects, qt_configurations
|
||||
-- recovery.not-started: check first; CREATE TABLE and ADD COLUMN fail if already exist
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP FOREIGN KEY fk_qt_configurations_project_uuid; ALTER TABLE qt_configurations DROP COLUMN project_uuid; DROP TABLE IF EXISTS qt_projects;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: qt_projects table missing | SELECT 1 FROM information_schema.TABLES WHERE table_schema=DATABASE() AND table_name='qt_projects' HAVING COUNT(*)=0
|
||||
|
||||
-- Add projects and attach configurations to projects
|
||||
|
||||
CREATE TABLE qt_projects (
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_pricelist_sync_status
|
||||
-- recovery.not-started: safe to re-run; CREATE TABLE IF NOT EXISTS
|
||||
-- recovery.partial: DROP TABLE IF EXISTS qt_pricelist_sync_status;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: qt_pricelist_sync_status table missing | SELECT 1 FROM information_schema.TABLES WHERE table_schema=DATABASE() AND table_name='qt_pricelist_sync_status' HAVING COUNT(*)=0
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qt_pricelist_sync_status (
|
||||
username VARCHAR(100) NOT NULL,
|
||||
last_sync_at DATETIME NOT NULL,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if pricelist_id already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN pricelist_id;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: pricelist_id column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='pricelist_id' HAVING COUNT(*)=0
|
||||
|
||||
-- Add pricelist binding to configurations
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN pricelist_id BIGINT UNSIGNED NULL AFTER server_count;
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_pricelist_sync_status
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_pricelist_sync_status DROP COLUMN app_version;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: app_version column in qt_pricelist_sync_status missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_pricelist_sync_status' AND column_name='app_version' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_pricelist_sync_status
|
||||
ADD COLUMN IF NOT EXISTS app_version VARCHAR(64) NULL;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_projects
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if tracker_url already exists
|
||||
-- recovery.partial: ALTER TABLE qt_projects DROP COLUMN tracker_url;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: tracker_url column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_projects' AND column_name='tracker_url' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_projects
|
||||
ADD COLUMN tracker_url VARCHAR(500) NULL AFTER name;
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_pricelists
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_pricelists DROP COLUMN source;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: source column in qt_pricelists missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_pricelists' AND column_name='source' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_pricelists
|
||||
ADD COLUMN IF NOT EXISTS source ENUM('estimate', 'warehouse', 'competitor') NOT NULL DEFAULT 'estimate' AFTER id;
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: stock_log
|
||||
-- recovery.not-started: safe to re-run; CREATE TABLE IF NOT EXISTS
|
||||
-- recovery.partial: DROP TABLE IF EXISTS stock_log;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: stock_log table missing | SELECT 1 FROM information_schema.TABLES WHERE table_schema=DATABASE() AND table_name='stock_log' HAVING COUNT(*)=0
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_log (
|
||||
stock_log_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
lot VARCHAR(255) NOT NULL,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN warehouse_pricelist_id, DROP COLUMN competitor_pricelist_id;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: warehouse_pricelist_id column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='warehouse_pricelist_id' HAVING COUNT(*)=0
|
||||
|
||||
-- Add per-source pricelist bindings for configurations
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS warehouse_pricelist_id BIGINT UNSIGNED NULL AFTER pricelist_id,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: stock_ignore_rules
|
||||
-- recovery.not-started: safe to re-run; CREATE TABLE IF NOT EXISTS
|
||||
-- recovery.partial: DROP TABLE IF EXISTS stock_ignore_rules;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: stock_ignore_rules table missing | SELECT 1 FROM information_schema.TABLES WHERE table_schema=DATABASE() AND table_name='stock_ignore_rules' HAVING COUNT(*)=0
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_ignore_rules (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
target VARCHAR(20) NOT NULL,
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: stock_log
|
||||
-- recovery.not-started: check first; CHANGE COLUMN fails if partnumber already exists
|
||||
-- recovery.partial: ALTER TABLE stock_log CHANGE COLUMN partnumber lot VARCHAR(255) NOT NULL;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: partnumber column in stock_log missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='stock_log' AND column_name='partnumber' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE stock_log
|
||||
CHANGE COLUMN lot partnumber VARCHAR(255) NOT NULL;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN only_in_stock;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: only_in_stock column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='only_in_stock' HAVING COUNT(*)=0
|
||||
|
||||
-- Add only_in_stock toggle to configuration settings persisted in MariaDB.
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS only_in_stock BOOLEAN NOT NULL DEFAULT FALSE AFTER disable_price_refresh;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_pricelist_items
|
||||
-- recovery.not-started: safe to re-run; uses INFORMATION_SCHEMA check before adding index
|
||||
-- recovery.partial: DROP INDEX IF EXISTS idx_qt_pricelist_items_pricelist_lot ON qt_pricelist_items;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: composite index on qt_pricelist_items missing | SELECT 1 FROM information_schema.STATISTICS WHERE table_schema=DATABASE() AND table_name='qt_pricelist_items' AND index_name='idx_qt_pricelist_items_pricelist_lot' HAVING COUNT(*)=0
|
||||
|
||||
-- Ensure fast lookup for /api/quote/price-levels batched queries:
|
||||
-- SELECT ... FROM qt_pricelist_items WHERE pricelist_id = ? AND lot_name IN (...)
|
||||
SET @has_idx := (
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN article;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: article column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='article' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS article VARCHAR(80) NULL AFTER server_count;
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN server_model;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: server_model column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='server_model' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS server_model VARCHAR(100) NULL AFTER server_count;
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN support_code;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: support_code column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='support_code' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS support_code VARCHAR(20) NULL AFTER server_model;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_projects
|
||||
-- recovery.not-started: check first; idempotent backfill but ADD COLUMN fails if code already exists
|
||||
-- recovery.partial: ALTER TABLE qt_projects DROP INDEX idx_qt_projects_code; ALTER TABLE qt_projects DROP COLUMN code;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: code column in qt_projects missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_projects' AND column_name='code' HAVING COUNT(*)=0
|
||||
|
||||
-- Add project code and enforce uniqueness
|
||||
|
||||
ALTER TABLE qt_projects
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_projects
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if variant already exists
|
||||
-- recovery.partial: DROP INDEX IF EXISTS idx_qt_projects_code_variant ON qt_projects; ALTER TABLE qt_projects DROP COLUMN variant;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: variant column in qt_projects missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_projects' AND column_name='variant' HAVING COUNT(*)=0
|
||||
|
||||
-- Add project variant and reset codes from project names
|
||||
|
||||
ALTER TABLE qt_projects
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_projects
|
||||
-- recovery.not-started: safe to re-run; MODIFY COLUMN is idempotent
|
||||
-- recovery.partial: ALTER TABLE qt_projects MODIFY COLUMN name VARCHAR(200) NOT NULL;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: name column in qt_projects is still NOT NULL | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_projects' AND column_name='name' AND is_nullable='NO' HAVING COUNT(*)>0
|
||||
|
||||
-- Allow NULL project names
|
||||
|
||||
ALTER TABLE qt_projects
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: safe to re-run; ADD COLUMN IF NOT EXISTS
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN line_no;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: line_no column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='line_no' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN IF NOT EXISTS line_no INT NULL AFTER only_in_stock;
|
||||
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
-- Tables affected: qt_configurations
|
||||
-- recovery.not-started: check first; ADD COLUMN fails if config_type already exists
|
||||
-- recovery.partial: ALTER TABLE qt_configurations DROP COLUMN config_type;
|
||||
-- recovery.completed: no action needed
|
||||
-- verify: config_type column missing | SELECT 1 FROM information_schema.COLUMNS WHERE table_schema=DATABASE() AND table_name='qt_configurations' AND column_name='config_type' HAVING COUNT(*)=0
|
||||
|
||||
ALTER TABLE qt_configurations
|
||||
ADD COLUMN config_type VARCHAR(20) NOT NULL DEFAULT 'server';
|
||||
|
||||
Reference in New Issue
Block a user