v1.6.5: fix attendance unique constraint, schema sync, seed script

- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-05-17 16:21:34 +02:00
parent 1db5060c42
commit cd6d3daf43
75 changed files with 1748 additions and 1169 deletions

View File

@@ -1,2 +0,0 @@
-- Add billing_text column to invoices table
ALTER TABLE `invoices` ADD COLUMN `billing_text` VARCHAR(500) NULL AFTER `issued_by`;

View File

@@ -1,3 +0,0 @@
-- Add lock fields to quotations table for pessimistic locking
ALTER TABLE `quotations` ADD COLUMN `locked_by` INT NULL AFTER `scope_description`;
ALTER TABLE `quotations` ADD COLUMN `locked_at` DATETIME NULL AFTER `locked_by`;

View File

@@ -1,9 +0,0 @@
CREATE TABLE `invoice_alert_log` (
`id` INT NOT NULL AUTO_INCREMENT,
`invoice_type` VARCHAR(20) NOT NULL,
`invoice_id` INT NOT NULL,
`alert_type` VARCHAR(20) NOT NULL,
`sent_at` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `invoice_type_invoice_id_alert_type` (`invoice_type`, `invoice_id`, `alert_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@@ -1 +0,0 @@
ALTER TABLE `invoices` ADD COLUMN `language` VARCHAR(5) DEFAULT 'cs' AFTER `billing_text`;

View File

@@ -1,2 +0,0 @@
-- Add file_path column for NAS storage of received invoice files
ALTER TABLE `received_invoices` ADD COLUMN `file_path` VARCHAR(500) NULL AFTER `file_data`;

View File

@@ -1,4 +0,0 @@
-- Remove file_data (BLOB) and file_path columns — files are now stored on NAS
-- Path is derived from year, month, and file_name
ALTER TABLE `received_invoices` DROP COLUMN `file_data`;
ALTER TABLE `received_invoices` DROP COLUMN `file_path`;

View File

@@ -1 +0,0 @@
ALTER TABLE `company_settings` ADD COLUMN `logo_data_dark` MEDIUMBLOB NULL AFTER `logo_data`;

View File

@@ -1,4 +0,0 @@
ALTER TABLE `company_settings`
ADD COLUMN `offer_number_pattern` VARCHAR(100) NULL,
ADD COLUMN `order_number_pattern` VARCHAR(100) NULL,
ADD COLUMN `invoice_number_pattern` VARCHAR(100) NULL;

View File

@@ -1,3 +0,0 @@
ALTER TABLE `company_settings`
ADD COLUMN `smtp_from` VARCHAR(255) NULL,
ADD COLUMN `smtp_from_name` VARCHAR(255) NULL;

View File

@@ -1,13 +0,0 @@
-- System settings columns on company_settings
ALTER TABLE `company_settings`
ADD COLUMN `break_threshold_hours` DECIMAL(4,2) DEFAULT 6,
ADD COLUMN `break_duration_short` INT DEFAULT 15,
ADD COLUMN `break_duration_long` INT DEFAULT 30,
ADD COLUMN `clock_rounding_minutes` INT DEFAULT 15,
ADD COLUMN `invoice_alert_email` VARCHAR(255) NULL,
ADD COLUMN `leave_notify_email` VARCHAR(255) NULL,
ADD COLUMN `max_login_attempts` INT DEFAULT 5,
ADD COLUMN `lockout_minutes` INT DEFAULT 15,
ADD COLUMN `max_requests_per_minute` INT DEFAULT 300,
ADD COLUMN `available_vat_rates` LONGTEXT NULL,
ADD COLUMN `available_currencies` LONGTEXT NULL;

View File

@@ -1,18 +0,0 @@
-- Create new unified permission
INSERT INTO permissions (name, display_name, description, module)
VALUES ('settings.manage', 'Správa nastavení', 'Správa všech nastavení systému', 'settings')
ON DUPLICATE KEY UPDATE display_name = VALUES(display_name);
-- Grant to all roles that had any of the old 3
INSERT IGNORE INTO role_permissions (role_id, permission_id)
SELECT DISTINCT rp.role_id, (SELECT id FROM permissions WHERE name = 'settings.manage')
FROM role_permissions rp
JOIN permissions p ON p.id = rp.permission_id
WHERE p.name IN ('offers.settings', 'settings.roles', 'settings.security');
-- Clean up old role_permissions
DELETE FROM role_permissions
WHERE permission_id IN (SELECT id FROM permissions WHERE name IN ('offers.settings', 'settings.roles', 'settings.security'));
-- Remove old permissions
DELETE FROM permissions WHERE name IN ('offers.settings', 'settings.roles', 'settings.security');

View File

@@ -1,2 +0,0 @@
-- Add unique constraint on number_sequences(type, year) for atomic numbering
ALTER TABLE number_sequences ADD UNIQUE INDEX idx_number_sequences_type_year (`type`, `year`);

View File

@@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE `users` ADD COLUMN `totp_last_used_counter` INTEGER NULL;

View File

@@ -98,6 +98,7 @@ CREATE TABLE `company_settings` (
`vat_id` VARCHAR(50) NULL,
`custom_fields` LONGTEXT NULL,
`logo_data` LONGBLOB NULL,
`logo_data_dark` MEDIUMBLOB NULL,
`quotation_prefix` VARCHAR(20) NULL,
`default_currency` VARCHAR(10) NULL DEFAULT 'CZK',
`default_vat_rate` DECIMAL(5, 2) NULL DEFAULT 21.00,
@@ -108,7 +109,24 @@ CREATE TABLE `company_settings` (
`order_type_code` VARCHAR(10) NULL,
`invoice_type_code` VARCHAR(10) NULL,
`require_2fa` BOOLEAN NOT NULL DEFAULT false,
`break_threshold_hours` DECIMAL(4, 2) NULL DEFAULT 6.00,
`break_duration_short` INTEGER NULL DEFAULT 15,
`break_duration_long` INTEGER NULL DEFAULT 30,
`clock_rounding_minutes` INTEGER NULL DEFAULT 15,
`invoice_alert_email` VARCHAR(255) NULL,
`leave_notify_email` VARCHAR(255) NULL,
`max_login_attempts` INTEGER NULL DEFAULT 5,
`lockout_minutes` INTEGER NULL DEFAULT 15,
`max_requests_per_minute` INTEGER NULL DEFAULT 300,
`available_vat_rates` LONGTEXT NULL,
`available_currencies` LONGTEXT NULL,
`smtp_from` VARCHAR(255) NULL,
`smtp_from_name` VARCHAR(255) NULL,
`offer_number_pattern` VARCHAR(100) NULL,
`order_number_pattern` VARCHAR(100) NULL,
`invoice_number_pattern` VARCHAR(100) NULL,
UNIQUE INDEX `company_settings_uuid_key`(`uuid`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -167,14 +185,18 @@ CREATE TABLE `invoices` (
`tax_date` DATE NULL,
`paid_date` DATE NULL,
`issued_by` VARCHAR(255) NULL,
`billing_text` VARCHAR(500) NULL,
`language` VARCHAR(5) NULL DEFAULT 'cs',
`notes` TEXT NULL,
`internal_notes` TEXT NULL,
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
`modified_at` DATETIME(0) NULL,
UNIQUE INDEX `idx_invoices_number_unique`(`invoice_number`),
INDEX `customer_id`(`customer_id`),
INDEX `idx_invoices_due_date`(`due_date`),
INDEX `idx_invoices_status_issue`(`status`, `issue_date`),
INDEX `idx_invoices_status_due`(`status`, `due_date`),
INDEX `order_id`(`order_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -239,6 +261,7 @@ CREATE TABLE `number_sequences` (
`year` INTEGER NULL,
`last_number` INTEGER NULL DEFAULT 0,
UNIQUE INDEX `idx_number_sequences_type_year`(`type`, `year`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -294,6 +317,7 @@ CREATE TABLE `orders` (
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
`modified_at` DATETIME(0) NULL,
UNIQUE INDEX `idx_orders_number_unique`(`order_number`),
INDEX `customer_id`(`customer_id`),
INDEX `quotation_id`(`quotation_id`),
PRIMARY KEY (`id`)
@@ -341,6 +365,7 @@ CREATE TABLE `projects` (
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
`modified_at` DATETIME(0) NULL,
UNIQUE INDEX `projects_project_number_key`(`project_number`),
INDEX `customer_id`(`customer_id`),
INDEX `fk_projects_responsible_user`(`responsible_user_id`),
INDEX `order_id`(`order_id`),
@@ -386,10 +411,13 @@ CREATE TABLE `quotations` (
`status` VARCHAR(20) NOT NULL DEFAULT 'active',
`scope_title` VARCHAR(500) NULL,
`scope_description` TEXT NULL,
`locked_by` INTEGER NULL,
`locked_at` DATETIME(0) NULL,
`uuid` VARCHAR(36) NULL,
`modified_at` DATETIME(0) NULL,
`sync_version` INTEGER NULL DEFAULT 0,
UNIQUE INDEX `quotations_quotation_number_key`(`quotation_number`),
INDEX `customer_id`(`customer_id`),
INDEX `idx_quotations_number`(`quotation_number`),
PRIMARY KEY (`id`)
@@ -411,7 +439,6 @@ CREATE TABLE `received_invoices` (
`due_date` DATE NULL,
`paid_date` DATE NULL,
`status` ENUM('unpaid', 'paid') NOT NULL DEFAULT 'unpaid',
`file_data` MEDIUMBLOB NULL,
`file_name` VARCHAR(255) NULL,
`file_mime` VARCHAR(100) NULL,
`file_size` INTEGER UNSIGNED NULL,
@@ -572,6 +599,7 @@ CREATE TABLE `users` (
`totp_secret` VARCHAR(255) NULL,
`totp_enabled` BOOLEAN NOT NULL DEFAULT false,
`totp_backup_codes` TEXT NULL,
`totp_last_used_counter` INTEGER NULL,
UNIQUE INDEX `username`(`username`),
UNIQUE INDEX `email`(`email`),
@@ -597,12 +625,28 @@ CREATE TABLE `vehicles` (
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `invoice_alert_log` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`invoice_type` VARCHAR(20) NOT NULL,
`invoice_id` INTEGER NOT NULL,
`alert_type` VARCHAR(20) NOT NULL,
`sent_at` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
`created_at` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
UNIQUE INDEX `invoice_type_invoice_id_alert_type`(`invoice_type`, `invoice_id`, `alert_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `attendance` ADD CONSTRAINT `attendance_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE `attendance_project_logs` ADD CONSTRAINT `attendance_project_logs_attendance_id_fkey` FOREIGN KEY (`attendance_id`) REFERENCES `attendance`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE `attendance_project_logs` ADD CONSTRAINT `attendance_project_logs_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE `invoice_items` ADD CONSTRAINT `invoice_items_ibfk_1` FOREIGN KEY (`invoice_id`) REFERENCES `invoices`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
@@ -674,4 +718,3 @@ ALTER TABLE `trips` ADD CONSTRAINT `trips_vehicle_fk` FOREIGN KEY (`vehicle_id`)
-- AddForeignKey
ALTER TABLE `users` ADD CONSTRAINT `users_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION;

View File

@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
# It should be added in your version-control system (e.g., Git)
provider = "mysql"