From 50f77e8a11f6d82dc3ddbe68c1b9e741e4b60f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=84=B1=EC=9D=98=20Macbook=20Pro?= Date: Wed, 16 Sep 2026 12:07:23 +0900 Subject: [PATCH] =?UTF-8?q?fix(migration):=20=ED=94=84=EB=A6=AC=EC=85=8B?= =?UTF-8?q?=20=EA=B8=B0=EB=B3=B8=20=EA=B5=AC=EC=84=B1=20=EC=A0=81=EC=9E=AC?= =?UTF-8?q?=EA=B0=80=20=EB=B9=88=20DB=EC=97=90=EC=84=9C=20=EC=99=B8?= =?UTF-8?q?=EB=9E=98=ED=82=A4=EC=97=90=20=EA=B1=B8=EB=A6=AC=EB=8D=98=20?= =?UTF-8?q?=EA=B2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - feature_module 행은 시드가 넣는다 — 마이그레이션만 돌린 빈 DB(CI 검증)에는 없어 FK 위반으로 배포 검증이 멈췄다 - 있는 모듈만 넣도록(WHERE EXISTS) 바꾸고, 시드(access_control.csv)에도 프리셋 74행을 넣어 새로 깔아도 같게 - 빈 DB에서 migrate deploy → seed → migrate diff 까지 로컬로 확인 Co-Authored-By: Claude Opus 5 --- .../20260916040000_preset_rows/migration.sql | 157 +++++++++--------- backend/prisma/seed.ts | 3 +- db/seed/access_control.csv | 74 +++++++++ 3 files changed, 158 insertions(+), 76 deletions(-) diff --git a/backend/prisma/migrations/20260916040000_preset_rows/migration.sql b/backend/prisma/migrations/20260916040000_preset_rows/migration.sql index d074bff..eb1e60e 100644 --- a/backend/prisma/migrations/20260916040000_preset_rows/migration.sql +++ b/backend/prisma/migrations/20260916040000_preset_rows/migration.sql @@ -1,78 +1,85 @@ -- 역할 프리셋 기본 구성을 행으로 적재한다(2026-09-16) — 이제 기능모듈 화면에서 자리마다 켜고 끌 수 있다. -- 코드(shared/rbac/presets.ts)의 값을 그대로 옮긴 것이고, 이 행이 하나도 없으면 코드 기본값이 쓰인다. -INSERT INTO "access_control" ("subject_type", "subject_ref", "module_code", "enabled", "memo") VALUES - ('preset', 'sales_mgr', 'addon.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'customer.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'doc.send.email', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'doc.send.sign', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'doc.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'notify.push', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'order.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'order.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'quote.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'quote.edit', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'stats.all', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'sales_mgr', 'stats.own', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'addon.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'basedata.dims', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'basedata.makerprice', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'basedata.optiondb', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'basedata.weights', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'checklist.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'doc.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'notify.push', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'order.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'order.control', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'order.remove', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'order.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'quote.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'quote.edit', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'stats.all', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'pm', 'stats.own', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'addon.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'basedata.makerprice', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'checklist.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'doc.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'notify.push', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'order.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'order.control', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'order.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'quote.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'quote.edit', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'stats.all', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'prod_mgr', 'stats.own', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'addon.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'doc.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'notify.push', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'order.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'quote.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'quote.edit', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'stats.all', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'exec', 'stats.own', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'account.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'addon.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'addon.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.dims', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.holiday', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.makerprice', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.optiondb', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'basedata.weights', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'checklist.manage', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'customer.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'doc.send.email', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'doc.send.sign', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'doc.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'notify.assign', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'notify.push', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'order.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'order.control', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'order.remove', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'order.view', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'quote.confirm', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'quote.create', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'quote.delete', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'quote.edit', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'stats.all', true, '프리셋 기본값 — 기능모듈 화면에서 고친다'), - ('preset', 'master', 'stats.own', true, '프리셋 기본값 — 기능모듈 화면에서 고친다') +-- +-- ⚠️ **없는 모듈은 건너뛴다.** 기능모듈 행은 시드(db/seed/feature_module.csv)가 넣는다 — 마이그레이션만 돌린 +-- 빈 DB(CI 검증)에는 아직 없어서 외래키에 걸렸다. 있는 것만 넣고, 나머지는 시드 뒤 화면에서 켠다. +INSERT INTO "access_control" ("subject_type", "subject_ref", "module_code", "enabled", "memo") +SELECT 'preset'::"SubjectType", v.ref, v.code, true, '프리셋 기본값 — 기능모듈 화면에서 고친다' +FROM (VALUES + ('sales_mgr', 'addon.view'), + ('sales_mgr', 'customer.view'), + ('sales_mgr', 'doc.send.email'), + ('sales_mgr', 'doc.send.sign'), + ('sales_mgr', 'doc.view'), + ('sales_mgr', 'notify.push'), + ('sales_mgr', 'order.confirm'), + ('sales_mgr', 'order.view'), + ('sales_mgr', 'quote.confirm'), + ('sales_mgr', 'quote.edit'), + ('sales_mgr', 'stats.all'), + ('sales_mgr', 'stats.own'), + ('pm', 'addon.manage'), + ('pm', 'basedata.dims'), + ('pm', 'basedata.makerprice'), + ('pm', 'basedata.optiondb'), + ('pm', 'basedata.weights'), + ('pm', 'checklist.manage'), + ('pm', 'doc.view'), + ('pm', 'notify.push'), + ('pm', 'order.confirm'), + ('pm', 'order.control'), + ('pm', 'order.remove'), + ('pm', 'order.view'), + ('pm', 'quote.confirm'), + ('pm', 'quote.edit'), + ('pm', 'stats.all'), + ('pm', 'stats.own'), + ('prod_mgr', 'addon.manage'), + ('prod_mgr', 'basedata.makerprice'), + ('prod_mgr', 'checklist.manage'), + ('prod_mgr', 'doc.view'), + ('prod_mgr', 'notify.push'), + ('prod_mgr', 'order.confirm'), + ('prod_mgr', 'order.control'), + ('prod_mgr', 'order.view'), + ('prod_mgr', 'quote.confirm'), + ('prod_mgr', 'quote.edit'), + ('prod_mgr', 'stats.all'), + ('prod_mgr', 'stats.own'), + ('exec', 'addon.view'), + ('exec', 'doc.view'), + ('exec', 'notify.push'), + ('exec', 'order.view'), + ('exec', 'quote.confirm'), + ('exec', 'quote.edit'), + ('exec', 'stats.all'), + ('exec', 'stats.own'), + ('master', 'account.manage'), + ('master', 'addon.manage'), + ('master', 'addon.view'), + ('master', 'basedata.dims'), + ('master', 'basedata.holiday'), + ('master', 'basedata.makerprice'), + ('master', 'basedata.manage'), + ('master', 'basedata.optiondb'), + ('master', 'basedata.weights'), + ('master', 'checklist.manage'), + ('master', 'customer.view'), + ('master', 'doc.send.email'), + ('master', 'doc.send.sign'), + ('master', 'doc.view'), + ('master', 'notify.assign'), + ('master', 'notify.push'), + ('master', 'order.confirm'), + ('master', 'order.control'), + ('master', 'order.remove'), + ('master', 'order.view'), + ('master', 'quote.confirm'), + ('master', 'quote.create'), + ('master', 'quote.delete'), + ('master', 'quote.edit'), + ('master', 'stats.all'), + ('master', 'stats.own') +) AS v(ref, code) +WHERE EXISTS (SELECT 1 FROM "feature_module" f WHERE f."code" = v.code) ON CONFLICT ("subject_type", "subject_ref", "module_code") DO NOTHING; diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts index fc8b832..334badd 100644 --- a/backend/prisma/seed.ts +++ b/backend/prisma/seed.ts @@ -99,7 +99,8 @@ async function main() { active: bool(r['active']), })); const acs = csv('access_control.csv').map(r => ({ - subject_type: r['subject_type'] as 'role' | 'user', + // 'preset' = 역할 프리셋 구성(관리자 안의 자리) — 2026-09-16 + subject_type: r['subject_type'] as 'role' | 'user' | 'preset', subject_ref: r['subject_ref']!, module_code: r['module_code']!, enabled: bool(r['enabled']), diff --git a/db/seed/access_control.csv b/db/seed/access_control.csv index 9e5a35b..234684f 100644 --- a/db/seed/access_control.csv +++ b/db/seed/access_control.csv @@ -28,3 +28,77 @@ role,MAKER,order.control,Y, role,MAKER,doc.view,Y, role,MAKER,notify.push,Y, role,ADMIN,customer.view,Y,프리셋 도입 전과 같게 — 관리자 기본 +preset,sales_mgr,addon.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,customer.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,doc.send.email,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,doc.send.sign,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,doc.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,notify.push,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,order.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,order.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,quote.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,quote.edit,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,stats.all,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,sales_mgr,stats.own,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,addon.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,basedata.dims,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,basedata.makerprice,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,basedata.optiondb,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,basedata.weights,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,checklist.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,doc.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,notify.push,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,order.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,order.control,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,order.remove,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,order.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,quote.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,quote.edit,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,stats.all,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,pm,stats.own,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,addon.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,basedata.makerprice,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,checklist.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,doc.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,notify.push,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,order.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,order.control,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,order.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,quote.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,quote.edit,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,stats.all,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,prod_mgr,stats.own,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,addon.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,doc.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,notify.push,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,order.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,quote.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,quote.edit,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,stats.all,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,exec,stats.own,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,account.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,addon.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,addon.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.dims,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.holiday,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.makerprice,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.optiondb,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,basedata.weights,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,checklist.manage,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,customer.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,doc.send.email,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,doc.send.sign,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,doc.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,notify.assign,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,notify.push,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,order.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,order.control,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,order.remove,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,order.view,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,quote.confirm,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,quote.create,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,quote.delete,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,quote.edit,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,stats.all,Y,프리셋 기본값 — 기능모듈 화면에서 고친다 +preset,master,stats.own,Y,프리셋 기본값 — 기능모듈 화면에서 고친다