diff --git a/prisma/migrations/20260609190000_drop_document_export_perms/migration.sql b/prisma/migrations/20260609190000_drop_document_export_perms/migration.sql new file mode 100644 index 0000000..88ca15a --- /dev/null +++ b/prisma/migrations/20260609190000_drop_document_export_perms/migration.sql @@ -0,0 +1,61 @@ +-- Drop the three DEAD document "export" permissions and make the 12 surviving +-- document permissions reproducible on a fresh production database. +-- +-- Why: `offers.export`, `orders.export`, `invoices.export` gate NOTHING on the +-- backend — every PDF/export route is guarded by `*.view`. They only ever +-- toggled frontend buttons and showed up as dead checkboxes in role management. +-- The 12 real keys (offers/orders/invoices x view/create/edit/delete) ARE each +-- enforced on >=1 route and must survive. +-- +-- All 15 keys were previously defined ONLY in prisma/seed.ts (dev-only), so a +-- fresh production DB never had ANY of them. This migration both removes the 3 +-- dead keys AND seeds the 12 survivors + their admin grant, mirroring the +-- prisma/seed.ts column set exactly (name, display_name, module, description, +-- created_at; role_permissions has only the composite PK). +-- +-- Idempotent: safe whether the rows exist or not, and safe on a prod DB that +-- never had them. Deletes are no-ops when absent; INSERT IGNORE skips on the +-- unique `permissions.name` key and on the `role_permissions` composite PK. + +-- 1. Remove the dead export role assignments first (FK-safe: children before +-- parents). No-op when the permissions/assignments don't exist. +DELETE FROM `role_permissions` +WHERE `permission_id` IN ( + SELECT `id` FROM `permissions` + WHERE `name` IN ('offers.export', 'orders.export', 'invoices.export') +); + +-- 2. Remove the dead export permissions themselves. +DELETE FROM `permissions` +WHERE `name` IN ('offers.export', 'orders.export', 'invoices.export'); + +-- 3. Idempotently (re)insert the 12 surviving document permissions. INSERT +-- IGNORE skips any that already exist (unique `name`). display_name / +-- description / module match prisma/seed.ts verbatim. +INSERT IGNORE INTO `permissions` (`name`, `display_name`, `module`, `description`, `created_at`) VALUES + ('offers.view', 'Zobrazit nabídky', 'offers', 'Prohlížet seznam nabídek', NOW()), + ('offers.create', 'Vytvořit nabídku', 'offers', 'Vytvářet nové nabídky', NOW()), + ('offers.edit', 'Upravit nabídku', 'offers', 'Upravovat existující nabídky', NOW()), + ('offers.delete', 'Smazat nabídku', 'offers', 'Mazat nabídky', NOW()), + ('orders.view', 'Zobrazit objednávky', 'orders', 'Prohlížet seznam objednávek', NOW()), + ('orders.create', 'Vytvořit objednávku', 'orders', 'Vytvářet nové objednávky', NOW()), + ('orders.edit', 'Upravit objednávku', 'orders', 'Upravovat existující objednávky', NOW()), + ('orders.delete', 'Smazat objednávku', 'orders', 'Mazat objednávky', NOW()), + ('invoices.view', 'Zobrazit faktury', 'invoices', 'Prohlížet seznam faktur', NOW()), + ('invoices.create', 'Vytvořit fakturu', 'invoices', 'Vytvářet nové faktury', NOW()), + ('invoices.edit', 'Upravit fakturu', 'invoices', 'Upravovat existující faktury', NOW()), + ('invoices.delete', 'Smazat fakturu', 'invoices', 'Mazat faktury', NOW()); + +-- 4. Grant all 12 survivors to the admin role. INSERT IGNORE on the composite +-- PK (role_id, permission_id) makes this idempotent and preserves any other +-- role assignments that already exist. +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT r.id, p.id +FROM `roles` r +CROSS JOIN `permissions` p +WHERE r.name = 'admin' + AND p.name IN ( + 'offers.view', 'offers.create', 'offers.edit', 'offers.delete', + 'orders.view', 'orders.create', 'orders.edit', 'orders.delete', + 'invoices.view', 'invoices.create', 'invoices.edit', 'invoices.delete' + ); diff --git a/prisma/seed.ts b/prisma/seed.ts index 65f2d97..cdbb541 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -95,12 +95,6 @@ const PERMISSIONS: { module: "offers", description: "Mazat nabídky", }, - { - name: "offers.export", - display_name: "Exportovat nabídku", - module: "offers", - description: "Exportovat nabídky do PDF", - }, // Objednávky { @@ -127,12 +121,6 @@ const PERMISSIONS: { module: "orders", description: "Mazat objednávky", }, - { - name: "orders.export", - display_name: "Exportovat objednávku", - module: "orders", - description: "Exportovat objednávky do PDF", - }, // Faktury { @@ -159,12 +147,6 @@ const PERMISSIONS: { module: "invoices", description: "Mazat faktury", }, - { - name: "invoices.export", - display_name: "Exportovat fakturu", - module: "invoices", - description: "Exportovat faktury do PDF", - }, // Projekty { diff --git a/src/admin/pages/InvoiceDetail.tsx b/src/admin/pages/InvoiceDetail.tsx index 0a58baf..ff94429 100644 --- a/src/admin/pages/InvoiceDetail.tsx +++ b/src/admin/pages/InvoiceDetail.tsx @@ -1147,7 +1147,7 @@ export default function InvoiceDetail() { - {hasPermission("invoices.export") && ( + {hasPermission("invoices.view") && ( ) )} - {hasPermission("orders.export") && ( + {hasPermission("orders.view") && (