@fayz-ai/db 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/canonical-schema.json +49 -0
- package/migrations/010_migration_ledger.sql +11 -1
- package/migrations/037_sync_tick_one_at_a_time.sql +256 -0
- package/migrations/038_onboarding_responses.sql +103 -0
- package/migrations/039_unit_tree.sql +270 -0
- package/migrations/040_resource_grants.sql +474 -0
- package/migrations/041_scoped_columns.sql +192 -0
- package/migrations/042_unit_scope_policies.sql +145 -0
- package/migrations/043_view_invoker.sql +90 -0
- package/migrations/044_unit_member_facts.sql +47 -0
- package/migrations/045_unit_entry.sql +236 -0
- package/migrations/046_membership_visible_to_members.sql +85 -0
- package/migrations/047_tasks.sql +266 -0
- package/migrations/048_every_login_is_a_person.sql +190 -0
- package/migrations/049_bookable_people.sql +126 -0
- package/migrations/050_authz_core.sql +230 -0
- package/migrations/051_authz_helpers.sql +330 -0
- package/migrations/052_authz_fanout.sql +253 -0
- package/migrations/053_authz_catalog_templates.sql +402 -0
- package/migrations/060_tenancy_seam.sql +132 -0
- package/migrations/061_create_tenant.sql +125 -0
- package/migrations/062_invitations.sql +329 -0
- package/migrations/063_legacy_tenancy_retired.sql +568 -0
- package/migrations/064_agent_guard_authz.sql +118 -0
- package/migrations/064b_supersede_unit_scope.sql +205 -0
- package/migrations/065_scaffold_sweep.sql +139 -0
- package/migrations/066_shadow_report.sql +122 -0
- package/migrations/070_permission_catalog.sql +205 -0
- package/migrations/071_v1_permission_map.sql +266 -0
- package/migrations/072_role_rpcs.sql +126 -0
- package/migrations/073_legacy_rbac_retire.sql +471 -0
- package/migrations/075_company_legal_entities.sql +169 -0
- package/migrations/076_tenant_settings.sql +251 -0
- package/migrations/080_people_roles.sql +269 -0
- package/migrations/081_people_identity.sql +271 -0
- package/migrations/082_people_membership_link.sql +142 -0
- package/migrations/083_people_data_migration.sql +75 -0
- package/migrations/084_people_registrations_tokens.sql +282 -0
- package/migrations/085_catalog_products.sql +333 -0
- package/migrations/086_catalog_pricing.sql +194 -0
- package/migrations/087_catalog_packages.sql +134 -0
- package/migrations/090_orders_venda.sql +166 -0
- package/migrations/091_orders_scaffold.sql +92 -0
- package/migrations/095_config_field_rules.sql +261 -0
- package/migrations/096_audit_append_only.sql +256 -0
- package/migrations/097_sequences_authorized.sql +111 -0
- package/migrations/098_holidays.sql +80 -0
- package/migrations/099_access_grants.sql +293 -0
- package/migrations/100_migration_schema.sql +217 -0
- package/migrations/101_migration_allowlist.sql +202 -0
- package/migrations/102_migration_rpcs.sql +933 -0
- package/migrations/103_migration_reconcile_ponr.sql +132 -0
- package/migrations/105_policies_to_authenticated.sql +71 -0
- package/migrations/106_function_search_path.sql +55 -0
- package/migrations/107_core_table_prefix.sql +361 -0
- package/migrations/108_authz_write_guards.sql +149 -0
- package/migrations/109_catalog_migration_promoted_columns.sql +126 -0
- package/migrations/110_people_kind_vocabulary.sql +120 -0
- package/migrations/111_migration_excluded_is_a_decision.sql +259 -0
- package/migrations/111_replacing_a_view_keeps_its_dependents.sql +132 -0
- package/migrations/112_money_precision_standard.sql +132 -0
- package/migrations/113_addresses_units_companies.sql +114 -0
- package/migrations/114_migrations_ledger_immutable.sql +65 -0
- package/migrations/115_registration_honours_field_rules.sql +172 -0
- package/migrations/116_field_rules_check_needs_a_tenant.sql +72 -0
- package/migrations/117_authz_reconciliation_schedule.sql +82 -0
- package/migrations/118_migration_catalog_writers.sql +450 -0
- package/migrations/119_migration_config_writers.sql +193 -0
- package/migrations/120_migration_facet_writer.sql +139 -0
- package/migrations/121_migration_financial_writers.sql +294 -0
- package/migrations/122_migration_inventory_writers.sql +245 -0
- package/migrations/123_migration_order_item_writer.sql +144 -0
- package/migrations/124_migration_ledger_writers.sql +170 -0
- package/migrations/125_migration_authz_people_writers.sql +205 -0
- package/migrations/126_owner_scoping_is_configuration.sql +247 -0
- package/migrations/127_scaffold_audit_hook.sql +98 -0
- package/migrations/128_shadow_tables_enforced.sql +93 -0
- package/migrations/129_order_kind_terminal_states.sql +57 -0
- package/migrations/130_every_login_is_a_person.sql +196 -0
- package/migrations/131_migration_invoice_split.sql +127 -0
- package/migrations/132_audit_prune_scheduled.sql +28 -0
- package/migrations/133_unit_is_a_session_property.sql +155 -0
- package/migrations/134_field_rules_on_read.sql +341 -0
- package/migrations/135_distribution_polarity.sql +158 -0
- package/migrations/136_unit_id_means_locality.sql +358 -0
- package/migrations/137_vendor_named_spine_objects.sql +126 -0
- package/migrations/138_grants_inherit_down_the_tree.sql +178 -0
- package/migrations/139_distributable_is_a_scaffold_option.sql +109 -0
- package/migrations/140_masking_is_a_scaffold_option.sql +85 -0
- package/migrations/141_renamed_objects_keep_a_compat_name.sql +192 -0
- package/migrations/142_a_person_has_addresses.sql +212 -0
- package/migrations/143_a_professional_has_a_rate.sql +75 -0
- package/migrations/144_a_product_can_be_sold_in_sizes.sql +57 -0
- package/migrations/145_the_app_says_which_tenant_it_is_in.sql +123 -0
- package/migrations/146_products_stock_has_one_writer.sql +67 -0
- package/migrations/147_every_archetype_has_a_kind_registry.sql +388 -0
- package/migrations/148_the_legacy_archetype_stops_being_writable.sql +154 -0
- package/migrations/149_the_event_log_gets_a_consumer.sql +331 -0
- package/migrations/150_the_app_is_configured_in_the_database.sql +285 -0
- package/migrations/151_stock_balance_is_an_archetype.sql +144 -0
- package/migrations/152_a_file_is_an_archetype.sql +222 -0
- package/migrations/153_a_plugin_calls_a_service_through_one_door.sql +199 -0
- package/migrations/154_the_plan_is_a_ceiling.sql +258 -0
- package/migrations/155_a_tenant_can_leave.sql +271 -0
- package/migrations/156_the_service_door_has_a_server_side.sql +335 -0
- package/migrations/157_nobody_creates_objects_in_public.sql +74 -0
- package/migrations/158_the_registry_owns_the_status_vocabulary.sql +153 -0
- package/migrations/159_the_plan_catalogue_gets_its_first_rows.sql +132 -0
- package/migrations/160_the_vertical_decides_what_starts_on.sql +246 -0
- package/migrations/161_a_tenant_leaves_with_its_data.sql +363 -0
- package/migrations/162_every_name_has_one_creator_for_real.sql +126 -0
- package/migrations/163_a_species_nobody_declared_goes_to_quarantine.sql +184 -0
- package/migrations/164_the_sequence_table_joins_the_register.sql +26 -0
- package/migrations/165_a_view_is_not_a_way_in.sql +90 -0
- package/migrations/166_the_platform_billing_leftovers_go.sql +72 -0
- package/migrations/167_a_view_gets_back_what_its_table_grants.sql +112 -0
- package/migrations/168_the_grant_census_stops_crying_wolf.sql +33 -0
- package/migrations/169_the_census_asks_postgres_instead_of_guessing.sql +45 -0
- package/package.json +6 -5
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- 070_permission_catalog.sql — the RBAC catalog seeded from declaredFeatures (#136).
|
|
3
|
+
--
|
|
4
|
+
-- GENERATED by scripts/emit-permission-catalog.mjs — do not edit by hand.
|
|
5
|
+
-- regenerate: pnpm emit:permission-catalog
|
|
6
|
+
-- verify: pnpm check:permission-catalog (CI: fails when out of date)
|
|
7
|
+
-- Once this file is recorded in a real database's ledger it is frozen: regenerate
|
|
8
|
+
-- into a NEW numbered file (--out …/0NN_permission_catalog_vK.sql), never here.
|
|
9
|
+
--
|
|
10
|
+
-- One key per {feature}.{action}; app.seed_permissions upserts and never removes
|
|
11
|
+
-- (removing a key is a decision, not a build). New keys reach every tenant's
|
|
12
|
+
-- system owner role through the permissions_grant_owner trigger (043).
|
|
13
|
+
--
|
|
14
|
+
-- Sources (plugin id ← manifest: features; [c] = conditional on config, [r] = referenced only):
|
|
15
|
+
-- admin ← plugins/plugin-admin/src/index.ts: admin
|
|
16
|
+
-- agenda ← plugins/plugin-agenda/src/index.ts: agenda.schedules, appointments
|
|
17
|
+
-- automations ← plugins/plugin-automations/src/index.ts: automations
|
|
18
|
+
-- blog ← plugins/plugin-blog/src/index.ts: blog
|
|
19
|
+
-- conversations ← plugins/plugin-conversations/src/index.ts: conversations
|
|
20
|
+
-- courses ← plugins/plugin-courses/src/index.ts: courses, courses.finance, courses.sales
|
|
21
|
+
-- crm ← plugins/plugin-crm/src/index.ts: crm.leads, crm.pipeline, crm.quotes, sales
|
|
22
|
+
-- dashboard ← plugins/plugin-dashboard/src/index.ts: dashboard
|
|
23
|
+
-- financial ← plugins/plugin-financial/src/index.ts: financial, financial.commissions[c], financial.payables[c], financial.receivables[c]
|
|
24
|
+
-- custom_forms ← plugins/plugin-forms/src/index.ts: custom_forms
|
|
25
|
+
-- inventory ← plugins/plugin-inventory/src/index.ts: inventory, inventory.recipes[c]
|
|
26
|
+
-- marketing ← plugins/plugin-marketing/src/index.ts: marketing, marketing.blog[c], marketing.content[c], marketing.landing-pages[c]
|
|
27
|
+
-- menu ← plugins/plugin-menu/src/index.ts: menu[r]
|
|
28
|
+
-- orders ← plugins/plugin-orders/src/index.ts: orders[r]
|
|
29
|
+
-- reports ← plugins/plugin-reports/src/index.ts: reports
|
|
30
|
+
-- reputation ← plugins/plugin-reputation/src/index.ts: reputation
|
|
31
|
+
-- scribe ← plugins/plugin-scribe/src/index.ts: scribe{read,create,edit,delete}
|
|
32
|
+
-- shop ← plugins/plugin-shop/src/index.ts: shop[r]
|
|
33
|
+
-- sites ← plugins/plugin-sites/src/index.ts: sites
|
|
34
|
+
-- tables ← plugins/plugin-tables/src/index.ts: tables[r]
|
|
35
|
+
-- ============================================================================
|
|
36
|
+
|
|
37
|
+
SELECT app.seed_permissions('[
|
|
38
|
+
{"key":"admin.create","plugin":"admin"},
|
|
39
|
+
{"key":"admin.delete","plugin":"admin"},
|
|
40
|
+
{"key":"admin.edit","plugin":"admin"},
|
|
41
|
+
{"key":"admin.manage","plugin":"admin"},
|
|
42
|
+
{"key":"admin.read","plugin":"admin"},
|
|
43
|
+
{"key":"agenda.schedules.create","plugin":"agenda","description":"Schedule Management: create"},
|
|
44
|
+
{"key":"agenda.schedules.delete","plugin":"agenda","description":"Schedule Management: delete"},
|
|
45
|
+
{"key":"agenda.schedules.edit","plugin":"agenda","description":"Schedule Management: edit"},
|
|
46
|
+
{"key":"agenda.schedules.manage","plugin":"agenda","description":"Schedule Management: manage"},
|
|
47
|
+
{"key":"agenda.schedules.read","plugin":"agenda","description":"Schedule Management: read"},
|
|
48
|
+
{"key":"appointments.create","plugin":"agenda"},
|
|
49
|
+
{"key":"appointments.delete","plugin":"agenda"},
|
|
50
|
+
{"key":"appointments.edit","plugin":"agenda"},
|
|
51
|
+
{"key":"appointments.manage","plugin":"agenda"},
|
|
52
|
+
{"key":"appointments.read","plugin":"agenda"},
|
|
53
|
+
{"key":"automations.create","plugin":"automations","description":"Automations: create"},
|
|
54
|
+
{"key":"automations.delete","plugin":"automations","description":"Automations: delete"},
|
|
55
|
+
{"key":"automations.edit","plugin":"automations","description":"Automations: edit"},
|
|
56
|
+
{"key":"automations.manage","plugin":"automations","description":"Automations: manage"},
|
|
57
|
+
{"key":"automations.read","plugin":"automations","description":"Automations: read"},
|
|
58
|
+
{"key":"blog.create","plugin":"blog","description":"Blog: create"},
|
|
59
|
+
{"key":"blog.delete","plugin":"blog","description":"Blog: delete"},
|
|
60
|
+
{"key":"blog.edit","plugin":"blog","description":"Blog: edit"},
|
|
61
|
+
{"key":"blog.manage","plugin":"blog","description":"Blog: manage"},
|
|
62
|
+
{"key":"blog.read","plugin":"blog","description":"Blog: read"},
|
|
63
|
+
{"key":"conversations.create","plugin":"conversations","description":"Conversations: create"},
|
|
64
|
+
{"key":"conversations.delete","plugin":"conversations","description":"Conversations: delete"},
|
|
65
|
+
{"key":"conversations.edit","plugin":"conversations","description":"Conversations: edit"},
|
|
66
|
+
{"key":"conversations.manage","plugin":"conversations","description":"Conversations: manage"},
|
|
67
|
+
{"key":"conversations.read","plugin":"conversations","description":"Conversations: read"},
|
|
68
|
+
{"key":"courses.create","plugin":"courses","description":"Courses: create"},
|
|
69
|
+
{"key":"courses.delete","plugin":"courses","description":"Courses: delete"},
|
|
70
|
+
{"key":"courses.edit","plugin":"courses","description":"Courses: edit"},
|
|
71
|
+
{"key":"courses.finance.create","plugin":"courses","description":"Financial: create"},
|
|
72
|
+
{"key":"courses.finance.delete","plugin":"courses","description":"Financial: delete"},
|
|
73
|
+
{"key":"courses.finance.edit","plugin":"courses","description":"Financial: edit"},
|
|
74
|
+
{"key":"courses.finance.manage","plugin":"courses","description":"Financial: manage"},
|
|
75
|
+
{"key":"courses.finance.read","plugin":"courses","description":"Financial: read"},
|
|
76
|
+
{"key":"courses.manage","plugin":"courses","description":"Courses: manage"},
|
|
77
|
+
{"key":"courses.read","plugin":"courses","description":"Courses: read"},
|
|
78
|
+
{"key":"courses.sales.create","plugin":"courses","description":"Sales: create"},
|
|
79
|
+
{"key":"courses.sales.delete","plugin":"courses","description":"Sales: delete"},
|
|
80
|
+
{"key":"courses.sales.edit","plugin":"courses","description":"Sales: edit"},
|
|
81
|
+
{"key":"courses.sales.manage","plugin":"courses","description":"Sales: manage"},
|
|
82
|
+
{"key":"courses.sales.read","plugin":"courses","description":"Sales: read"},
|
|
83
|
+
{"key":"crm.leads.create","plugin":"crm"},
|
|
84
|
+
{"key":"crm.leads.delete","plugin":"crm"},
|
|
85
|
+
{"key":"crm.leads.edit","plugin":"crm"},
|
|
86
|
+
{"key":"crm.leads.manage","plugin":"crm"},
|
|
87
|
+
{"key":"crm.leads.read","plugin":"crm"},
|
|
88
|
+
{"key":"crm.pipeline.create","plugin":"crm"},
|
|
89
|
+
{"key":"crm.pipeline.delete","plugin":"crm"},
|
|
90
|
+
{"key":"crm.pipeline.edit","plugin":"crm"},
|
|
91
|
+
{"key":"crm.pipeline.manage","plugin":"crm"},
|
|
92
|
+
{"key":"crm.pipeline.read","plugin":"crm"},
|
|
93
|
+
{"key":"crm.quotes.create","plugin":"crm"},
|
|
94
|
+
{"key":"crm.quotes.delete","plugin":"crm"},
|
|
95
|
+
{"key":"crm.quotes.edit","plugin":"crm"},
|
|
96
|
+
{"key":"crm.quotes.manage","plugin":"crm"},
|
|
97
|
+
{"key":"crm.quotes.read","plugin":"crm"},
|
|
98
|
+
{"key":"custom_forms.create","plugin":"custom_forms"},
|
|
99
|
+
{"key":"custom_forms.delete","plugin":"custom_forms"},
|
|
100
|
+
{"key":"custom_forms.edit","plugin":"custom_forms"},
|
|
101
|
+
{"key":"custom_forms.manage","plugin":"custom_forms"},
|
|
102
|
+
{"key":"custom_forms.read","plugin":"custom_forms"},
|
|
103
|
+
{"key":"dashboard.create","plugin":"dashboard"},
|
|
104
|
+
{"key":"dashboard.delete","plugin":"dashboard"},
|
|
105
|
+
{"key":"dashboard.edit","plugin":"dashboard"},
|
|
106
|
+
{"key":"dashboard.manage","plugin":"dashboard"},
|
|
107
|
+
{"key":"dashboard.read","plugin":"dashboard"},
|
|
108
|
+
{"key":"financial.commissions.create","plugin":"financial"},
|
|
109
|
+
{"key":"financial.commissions.delete","plugin":"financial"},
|
|
110
|
+
{"key":"financial.commissions.edit","plugin":"financial"},
|
|
111
|
+
{"key":"financial.commissions.manage","plugin":"financial"},
|
|
112
|
+
{"key":"financial.commissions.read","plugin":"financial"},
|
|
113
|
+
{"key":"financial.create","plugin":"financial"},
|
|
114
|
+
{"key":"financial.delete","plugin":"financial"},
|
|
115
|
+
{"key":"financial.edit","plugin":"financial"},
|
|
116
|
+
{"key":"financial.manage","plugin":"financial"},
|
|
117
|
+
{"key":"financial.payables.create","plugin":"financial"},
|
|
118
|
+
{"key":"financial.payables.delete","plugin":"financial"},
|
|
119
|
+
{"key":"financial.payables.edit","plugin":"financial"},
|
|
120
|
+
{"key":"financial.payables.manage","plugin":"financial"},
|
|
121
|
+
{"key":"financial.payables.read","plugin":"financial"},
|
|
122
|
+
{"key":"financial.read","plugin":"financial"},
|
|
123
|
+
{"key":"financial.read_all","plugin":"financial"},
|
|
124
|
+
{"key":"financial.receivables.create","plugin":"financial"},
|
|
125
|
+
{"key":"financial.receivables.delete","plugin":"financial"},
|
|
126
|
+
{"key":"financial.receivables.edit","plugin":"financial"},
|
|
127
|
+
{"key":"financial.receivables.manage","plugin":"financial"},
|
|
128
|
+
{"key":"financial.receivables.read","plugin":"financial"},
|
|
129
|
+
{"key":"inventory.create","plugin":"inventory"},
|
|
130
|
+
{"key":"inventory.delete","plugin":"inventory"},
|
|
131
|
+
{"key":"inventory.edit","plugin":"inventory"},
|
|
132
|
+
{"key":"inventory.manage","plugin":"inventory"},
|
|
133
|
+
{"key":"inventory.read","plugin":"inventory"},
|
|
134
|
+
{"key":"inventory.recipes.create","plugin":"inventory"},
|
|
135
|
+
{"key":"inventory.recipes.delete","plugin":"inventory"},
|
|
136
|
+
{"key":"inventory.recipes.edit","plugin":"inventory"},
|
|
137
|
+
{"key":"inventory.recipes.manage","plugin":"inventory"},
|
|
138
|
+
{"key":"inventory.recipes.read","plugin":"inventory"},
|
|
139
|
+
{"key":"marketing.blog.create","plugin":"marketing"},
|
|
140
|
+
{"key":"marketing.blog.delete","plugin":"marketing"},
|
|
141
|
+
{"key":"marketing.blog.edit","plugin":"marketing"},
|
|
142
|
+
{"key":"marketing.blog.manage","plugin":"marketing"},
|
|
143
|
+
{"key":"marketing.blog.read","plugin":"marketing"},
|
|
144
|
+
{"key":"marketing.content.create","plugin":"marketing"},
|
|
145
|
+
{"key":"marketing.content.delete","plugin":"marketing"},
|
|
146
|
+
{"key":"marketing.content.edit","plugin":"marketing"},
|
|
147
|
+
{"key":"marketing.content.manage","plugin":"marketing"},
|
|
148
|
+
{"key":"marketing.content.read","plugin":"marketing"},
|
|
149
|
+
{"key":"marketing.create","plugin":"marketing"},
|
|
150
|
+
{"key":"marketing.delete","plugin":"marketing"},
|
|
151
|
+
{"key":"marketing.edit","plugin":"marketing"},
|
|
152
|
+
{"key":"marketing.landing-pages.create","plugin":"marketing"},
|
|
153
|
+
{"key":"marketing.landing-pages.delete","plugin":"marketing"},
|
|
154
|
+
{"key":"marketing.landing-pages.edit","plugin":"marketing"},
|
|
155
|
+
{"key":"marketing.landing-pages.manage","plugin":"marketing"},
|
|
156
|
+
{"key":"marketing.landing-pages.read","plugin":"marketing"},
|
|
157
|
+
{"key":"marketing.manage","plugin":"marketing"},
|
|
158
|
+
{"key":"marketing.read","plugin":"marketing"},
|
|
159
|
+
{"key":"menu.create","plugin":"menu"},
|
|
160
|
+
{"key":"menu.delete","plugin":"menu"},
|
|
161
|
+
{"key":"menu.edit","plugin":"menu"},
|
|
162
|
+
{"key":"menu.manage","plugin":"menu"},
|
|
163
|
+
{"key":"menu.read","plugin":"menu"},
|
|
164
|
+
{"key":"orders.create","plugin":"orders"},
|
|
165
|
+
{"key":"orders.delete","plugin":"orders"},
|
|
166
|
+
{"key":"orders.edit","plugin":"orders"},
|
|
167
|
+
{"key":"orders.manage","plugin":"orders"},
|
|
168
|
+
{"key":"orders.read","plugin":"orders"},
|
|
169
|
+
{"key":"orders.read_all","plugin":"orders"},
|
|
170
|
+
{"key":"reports.create","plugin":"reports"},
|
|
171
|
+
{"key":"reports.delete","plugin":"reports"},
|
|
172
|
+
{"key":"reports.edit","plugin":"reports"},
|
|
173
|
+
{"key":"reports.manage","plugin":"reports"},
|
|
174
|
+
{"key":"reports.read","plugin":"reports"},
|
|
175
|
+
{"key":"reputation.create","plugin":"reputation","description":"Reputation: create"},
|
|
176
|
+
{"key":"reputation.delete","plugin":"reputation","description":"Reputation: delete"},
|
|
177
|
+
{"key":"reputation.edit","plugin":"reputation","description":"Reputation: edit"},
|
|
178
|
+
{"key":"reputation.manage","plugin":"reputation","description":"Reputation: manage"},
|
|
179
|
+
{"key":"reputation.read","plugin":"reputation","description":"Reputation: read"},
|
|
180
|
+
{"key":"sales.create","plugin":"crm"},
|
|
181
|
+
{"key":"sales.delete","plugin":"crm"},
|
|
182
|
+
{"key":"sales.edit","plugin":"crm"},
|
|
183
|
+
{"key":"sales.manage","plugin":"crm"},
|
|
184
|
+
{"key":"sales.read","plugin":"crm"},
|
|
185
|
+
{"key":"scribe.create","plugin":"scribe"},
|
|
186
|
+
{"key":"scribe.delete","plugin":"scribe"},
|
|
187
|
+
{"key":"scribe.edit","plugin":"scribe"},
|
|
188
|
+
{"key":"scribe.manage","plugin":"scribe"},
|
|
189
|
+
{"key":"scribe.read","plugin":"scribe"},
|
|
190
|
+
{"key":"shop.create","plugin":"shop"},
|
|
191
|
+
{"key":"shop.delete","plugin":"shop"},
|
|
192
|
+
{"key":"shop.edit","plugin":"shop"},
|
|
193
|
+
{"key":"shop.manage","plugin":"shop"},
|
|
194
|
+
{"key":"shop.read","plugin":"shop"},
|
|
195
|
+
{"key":"sites.create","plugin":"sites","description":"Sites & Funnels: create"},
|
|
196
|
+
{"key":"sites.delete","plugin":"sites","description":"Sites & Funnels: delete"},
|
|
197
|
+
{"key":"sites.edit","plugin":"sites","description":"Sites & Funnels: edit"},
|
|
198
|
+
{"key":"sites.manage","plugin":"sites","description":"Sites & Funnels: manage"},
|
|
199
|
+
{"key":"sites.read","plugin":"sites","description":"Sites & Funnels: read"},
|
|
200
|
+
{"key":"tables.create","plugin":"tables"},
|
|
201
|
+
{"key":"tables.delete","plugin":"tables"},
|
|
202
|
+
{"key":"tables.edit","plugin":"tables"},
|
|
203
|
+
{"key":"tables.manage","plugin":"tables"},
|
|
204
|
+
{"key":"tables.read","plugin":"tables"}
|
|
205
|
+
]'::jsonb);
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- 071_v1_permission_map.sql — the V1 → V2 permission mapping as data (#136).
|
|
3
|
+
--
|
|
4
|
+
-- V1 (the-chef / beautyplace) had one catalog of snake_case codes × four flags
|
|
5
|
+
-- (can_view / can_insert / can_edit / can_delete) per "regra de permissão", an
|
|
6
|
+
-- admin/master/owner bypass in code and a per-tenant enforcement flag. V2 has
|
|
7
|
+
-- '{plugin}.{action}' keys, roles per tenant and RLS as the only law.
|
|
8
|
+
--
|
|
9
|
+
-- The mapping was produced by reading the V1 REPO (src/**, supabase/functions/**,
|
|
10
|
+
-- supabase/migrations/**; never a data dump): every code seeded by a migration
|
|
11
|
+
-- plus every code the code enforces, each decided as
|
|
12
|
+
-- mapped → a V2 feature (the four flags expand to read/create/edit/delete)
|
|
13
|
+
-- or one V2 key (any flag grants it), plus optional extra keys
|
|
14
|
+
-- backlog → V2 has no surface for it yet: nothing is granted, nothing is lost
|
|
15
|
+
-- in a way anyone can act on today (listed for the roadmap)
|
|
16
|
+
-- dropped → superseded/inactive in V1, or a bypass/flag V2 refuses to carry
|
|
17
|
+
-- The prose (why each row) is docs/data/phase-0/01b-v1-permission-mapping.md;
|
|
18
|
+
-- this table is what the extractor/ledger slice reads. Rows only, no client data.
|
|
19
|
+
--
|
|
20
|
+
-- app.v1_permission_keys(code, view, insert, edit, delete) is the one function
|
|
21
|
+
-- that turns a V1 rule detail into the V2 keys to grant — the extractor calls
|
|
22
|
+
-- it instead of re-implementing the expansion.
|
|
23
|
+
-- ============================================================================
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS app.v1_permission_map (
|
|
26
|
+
v1_code text PRIMARY KEY,
|
|
27
|
+
v1_category text,
|
|
28
|
+
v1_name text,
|
|
29
|
+
seeded_in_repo boolean NOT NULL DEFAULT true, -- inserted by a V1 migration (false ⇒ exists only in the live table)
|
|
30
|
+
active_in_v1 boolean, -- catalog `active` at the head of the V1 migrations (null when not seeded)
|
|
31
|
+
enforced_in_v1 boolean NOT NULL DEFAULT false, -- appears at an enforcement point (can()/usePermission/<Can>/permissionCode/user_has_permission)
|
|
32
|
+
status text NOT NULL CHECK (status IN ('mapped', 'backlog', 'dropped')),
|
|
33
|
+
kind text CHECK (kind IN ('feature', 'key')),
|
|
34
|
+
v2_feature text, -- kind='feature': view→.read, insert→.create, edit→.edit, delete→.delete (fallback .manage)
|
|
35
|
+
v2_key text REFERENCES app.permissions(key) ON DELETE SET NULL, -- kind='key': the key any true flag grants; kind='feature': the .read key
|
|
36
|
+
v2_extra_keys text[] NOT NULL DEFAULT '{}', -- granted alongside whenever any flag is true
|
|
37
|
+
note text,
|
|
38
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
39
|
+
CHECK (status <> 'mapped' OR (kind IS NOT NULL AND v2_key IS NOT NULL)),
|
|
40
|
+
CHECK (kind IS DISTINCT FROM 'feature' OR v2_feature IS NOT NULL),
|
|
41
|
+
CHECK (v1_code ~ '^[a-z][a-z0-9_]*$')
|
|
42
|
+
);
|
|
43
|
+
ALTER TABLE app.v1_permission_map ENABLE ROW LEVEL SECURITY;
|
|
44
|
+
GRANT SELECT ON app.v1_permission_map TO authenticated, service_role;
|
|
45
|
+
GRANT ALL ON app.v1_permission_map TO service_role;
|
|
46
|
+
DROP POLICY IF EXISTS v1_permission_map_select ON app.v1_permission_map;
|
|
47
|
+
CREATE POLICY v1_permission_map_select ON app.v1_permission_map FOR SELECT TO authenticated USING (true);
|
|
48
|
+
COMMENT ON TABLE app.v1_permission_map IS
|
|
49
|
+
'V1 permission code → V2 permission key mapping (#136). Read by the V1 extractor; prose in docs/data/phase-0/01b-v1-permission-mapping.md.';
|
|
50
|
+
|
|
51
|
+
-- ── the rows ────────────────────────────────────────────────────────────
|
|
52
|
+
-- Upsert on v1_code so a later file may refine a row (status/target/note)
|
|
53
|
+
-- without editing this one; nothing here deletes.
|
|
54
|
+
INSERT INTO app.v1_permission_map
|
|
55
|
+
(v1_code, v1_category, v1_name, seeded_in_repo, active_in_v1, enforced_in_v1, status, kind, v2_feature, v2_key, v2_extra_keys, note)
|
|
56
|
+
VALUES
|
|
57
|
+
('agenda_calendar', 'Agenda', 'Tela de Agendamento', true, true, true, 'mapped', 'feature', 'agenda', 'agenda.read', '{}'::text[], 'The agenda screen. RLS on public.appointments/schedules is keyed on agenda.* (043); plugin-agenda still gates its UI on appointments.* — see the divergence note.'),
|
|
58
|
+
('agenda_confirmation', 'Agenda', 'Confirmação de Agendamentos', true, true, true, 'mapped', 'feature', 'agenda', 'agenda.read', '{}'::text[], 'Sub-screen of the agenda (WhatsApp confirmations); coarser: V2 has no finer key yet.'),
|
|
59
|
+
('checkin_dashboard', 'Agenda', 'Check-in de Clientes', true, true, true, 'mapped', 'feature', 'agenda', 'agenda.read', '{}'::text[], 'Client check-in is an agenda flow (PRD 07 FieldRules check-in); coarser.'),
|
|
60
|
+
('agenda_reservations', 'Agenda', 'Reservas', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Table/slot reservations — plugin-tables (`tables.*`) once it declares features.'),
|
|
61
|
+
('agenda_schedules', 'Agenda', 'Escalas', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced in V1. Natural target: agenda.schedules.* (plugin-agenda declares it).'),
|
|
62
|
+
('appointments_confirm', 'Agenda', 'Confirmação de Agendamentos', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed; superseded by agenda_confirmation.'),
|
|
63
|
+
('appointments_schedule', 'Agenda', 'Tela de Agendamento', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed; superseded by agenda_calendar.'),
|
|
64
|
+
('clients_list', 'Clientes', 'Listar Clientes', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'The client list: view/insert/edit/delete map 1:1 to people.read/create/edit/delete.'),
|
|
65
|
+
('clients_create', 'Clientes', 'Cadastrar Cliente', true, true, true, 'mapped', 'key', NULL, 'people.create', '{}'::text[], 'The "new client" screen; any granted flag becomes people.create.'),
|
|
66
|
+
('clients_card', 'Clientes', 'Ficha do Cliente', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: people.read (+ people.read_all for the full record of others’ clients).'),
|
|
67
|
+
('clients_manage', 'Clientes', 'Gerenciar Clientes', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed; superseded by clients_list/clients_create.'),
|
|
68
|
+
('communication_dashboard', 'Comunicação', 'Comunicação', true, true, true, 'mapped', 'feature', 'automations', 'automations.read', '{}'::text[], 'V1 "Comunicação" (WhatsApp templates, triggers, dispatch log) is plugin-automations in V2 (messaging moved under Automações); coarser.'),
|
|
69
|
+
('communication_manual_confirmation', 'Comunicação', 'Confirmação Manual (Mensagem)', true, true, true, 'mapped', 'feature', 'automations', 'automations.read', '{}'::text[], 'Manual confirmation message config; coarser (automations.*).'),
|
|
70
|
+
('communication_dispatch_log', 'Comunicação', 'Log de Disparos', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: automations.read.'),
|
|
71
|
+
('communication_events', 'Comunicação', 'Eventos / Gatilhos', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: automations.manage.'),
|
|
72
|
+
('communication_templates', 'Comunicação', 'Templates de Mensagem', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: automations.manage.'),
|
|
73
|
+
('communication_whatsapp', 'Comunicação', 'WhatsApp Config', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: conversations.manage / settings.manage.'),
|
|
74
|
+
('config_communication', 'Configurações', 'Comunicação', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed.'),
|
|
75
|
+
('config_hub', 'Configurações', 'Hub de Configurações', true, true, true, 'mapped', 'feature', 'settings', 'settings.read', '{}'::text[], 'The settings hub: view→settings.read, edit→settings.manage.'),
|
|
76
|
+
('config_appearance', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'settings', 'settings.read', '{}'::text[], 'Branding (logo/name) — tenant settings (PRD 02 R5). Not seeded by any migration in the V1 repo (exists only in the live table).'),
|
|
77
|
+
('config_company', 'Configurações', 'Empresa', true, true, true, 'mapped', 'feature', 'units', 'units.read', '{}'::text[], 'Empresa = the legal entity a unit references (PRD 02 R2, #138): units.read/units.manage.'),
|
|
78
|
+
('config_company_type', 'Configurações', 'Tipo de Empresa', true, true, true, 'mapped', 'feature', 'tenant', 'tenant.read', '{}'::text[], 'Franchise mode → tenants.kind (ADR 0002): tenant.read/tenant.manage.'),
|
|
79
|
+
('config_cancellation_rules', 'Configurações', 'Regras de Cancelamento', true, true, true, 'mapped', 'feature', 'agenda', 'agenda.read', '{}'::text[], 'Appointment cancellation policy — agenda settings; coarser.'),
|
|
80
|
+
('config_holidays', 'Configurações', 'Feriados', true, true, true, 'mapped', 'feature', 'agenda', 'agenda.read', '{}'::text[], 'Holidays feed the agenda availability; coarser.'),
|
|
81
|
+
('config_chart_accounts', 'Configurações', 'Plano de Contas', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'plg_financial_chart_of_accounts is financial data: financial.read/manage.'),
|
|
82
|
+
('config_cost_centers', 'Configurações', 'Centros de Custo', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'plg_financial_cost_centers: financial.read/manage.'),
|
|
83
|
+
('config_payment_methods', 'Configurações', 'Formas de Recebimento', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'plg_financial_payment_methods: financial.read/manage.'),
|
|
84
|
+
('config_commission_rules', 'Configurações', 'Regras de Comissão', true, true, true, 'mapped', 'feature', 'financial.commissions', 'financial.commissions.read', '{}'::text[], 'Commission rules live with commissions.'),
|
|
85
|
+
('config_discount_rules', 'Configurações', 'Regras de Desconto', true, true, true, 'mapped', 'feature', 'orders', 'orders.read', '{}'::text[], 'Discount rules apply to orders/bills; coarser.'),
|
|
86
|
+
('config_cover_charge', 'Configurações', 'Taxas de Ingresso', true, true, true, 'mapped', 'feature', 'orders', 'orders.read', '{}'::text[], 'Cover charge is an order fee (chef); coarser.'),
|
|
87
|
+
('config_service_charge', 'Configurações', 'Taxa de Serviço', true, true, true, 'mapped', 'feature', 'orders', 'orders.read', '{}'::text[], 'Service charge is an order fee (chef); coarser.'),
|
|
88
|
+
('config_price_tables', 'Configurações', 'Tabelas de Preço', true, true, true, 'mapped', 'feature', 'catalog', 'catalog.read', '{}'::text[], 'Price tables are catalog pricing (PRD 04).'),
|
|
89
|
+
('config_price_variations', 'Configurações', 'Variações de Preço', true, true, true, 'mapped', 'feature', 'catalog', 'catalog.read', '{}'::text[], 'Price variations are catalog pricing (PRD 04).'),
|
|
90
|
+
('config_field_visibility', 'Configurações', 'Ocultação de Dados', true, true, true, 'mapped', 'feature', 'settings', 'settings.read', '{}'::text[], 'FieldRules (PRD 07): tenant configuration → settings.*.'),
|
|
91
|
+
('config_required_fields', 'Configurações', 'Campos Obrigatórios', true, true, true, 'mapped', 'feature', 'settings', 'settings.read', '{}'::text[], 'FieldRules (PRD 07): tenant configuration → settings.*.'),
|
|
92
|
+
('config_forms', 'Configurações', 'Formulários', true, true, true, 'mapped', 'feature', 'custom_forms', 'custom_forms.read', '{}'::text[], 'plugin-forms (custom_forms.*).'),
|
|
93
|
+
('config_permission_rules', 'Configurações', 'Regras de Permissão', true, true, true, 'mapped', 'feature', 'roles', 'roles.read', '{}'::text[], 'The permission-rules screen becomes the roles editor: roles.read/roles.manage.'),
|
|
94
|
+
('config_contracts', 'Configurações', 'Modelos de Contratos', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Contract templates: no V2 surface (plugin-crm has leads/quotes/pipeline, no contracts).'),
|
|
95
|
+
('config_qr_access', 'Configurações', 'Acesso por QR Code', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; QR-code unit access has no V2 surface.'),
|
|
96
|
+
('config_software_profile', 'Configurações', 'Perfil de Software', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; software profiles become tenant plugin configuration (PRD 02 R3), not a permission.'),
|
|
97
|
+
('crm_dashboard', 'Marketing', 'CRM', true, true, true, 'mapped', 'feature', 'sales', 'sales.read', '{}'::text[], 'plugin-crm’s main feature id is `sales`.'),
|
|
98
|
+
('marketing_dashboard', 'Marketing', 'Marketing', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: marketing.read.'),
|
|
99
|
+
('marketing_online_booking', 'Marketing', 'Agendamento Online', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Public booking config → agenda settings.'),
|
|
100
|
+
('dashboard_alerts', 'Dashboard', 'Alertas e Destaques', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget → dashboard.read.'),
|
|
101
|
+
('dashboard_appointments_status', 'Dashboard', 'Status dos Agendamentos', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget (V1 also inferred it from agenda_calendar).'),
|
|
102
|
+
('dashboard_kpi_operational', 'Dashboard', 'KPIs Operacionais', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget (V1 also inferred it from agenda_calendar).'),
|
|
103
|
+
('dashboard_top_services', 'Dashboard', 'Top Serviços', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget.'),
|
|
104
|
+
('dashboard_units_ranking', 'Dashboard', 'Ranking de Unidades', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget (unit scope comes from user_unit_access, not from a permission).'),
|
|
105
|
+
('dashboard_weekly_trend', 'Dashboard', 'Desempenho 7 Dias', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget.'),
|
|
106
|
+
('dashboard_monthly_trend', 'Dashboard', 'Tendência Mensal', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Operational widget.'),
|
|
107
|
+
('dashboard_goals', 'Dashboard', 'Metas de Vendas', true, true, true, 'mapped', 'feature', 'dashboard', 'dashboard.read', '{}'::text[], 'Sales goals widget.'),
|
|
108
|
+
('dashboard_kpi_consolidated', 'Dashboard', 'KPIs Consolidados', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'Money widget: V1 never let it inherit from the agenda; V2 gates money on financial.read.'),
|
|
109
|
+
('dashboard_payables', 'Dashboard', 'Contas a Pagar (Dashboard)', true, true, true, 'mapped', 'feature', 'financial.payables', 'financial.payables.read', '{}'::text[], 'Money widget → financial.payables.read.'),
|
|
110
|
+
('dashboard_receivables', 'Dashboard', 'Contas a Receber (Dashboard)', true, true, true, 'mapped', 'feature', 'financial.receivables', 'financial.receivables.read', '{}'::text[], 'Money widget → financial.receivables.read.'),
|
|
111
|
+
('dashboard_stock_critical', 'Dashboard', 'Estoque Crítico', true, true, true, 'mapped', 'feature', 'inventory', 'inventory.read', '{}'::text[], 'Stock widget → inventory.read.'),
|
|
112
|
+
('dashboard_revenue_by_service', 'Dashboard', 'Receita por Serviço', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: financial.read.'),
|
|
113
|
+
('financial_dashboard', 'Financeiro', 'Financeiro (Painel)', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'The financial panel.'),
|
|
114
|
+
('financial_payable', 'Financeiro', 'Contas a Pagar', true, true, true, 'mapped', 'feature', 'financial.payables', 'financial.payables.read', '{}'::text[], 'Contas a pagar.'),
|
|
115
|
+
('financial_receivable', 'Financeiro', 'Contas a Receber', true, true, true, 'mapped', 'feature', 'financial.receivables', 'financial.receivables.read', '{}'::text[], 'Contas a receber.'),
|
|
116
|
+
('financial_cash', 'Financeiro', 'Caixas', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', ARRAY['financial.read_all']::text[], 'Managerial view of every cash register of the units: financial.* plus read_all (sessions are owner-scoped rows).'),
|
|
117
|
+
('financial_cash_receipt', 'Financeiro', 'Receber pelo Caixinha', true, true, true, 'mapped', 'key', NULL, 'financial.cash_session', ARRAY['financial.create']::text[], 'Receive through the caixinha: open/operate a session + create movements.'),
|
|
118
|
+
('financial_cash_payment', 'Financeiro', 'Pagar pelo Caixinha', true, true, true, 'mapped', 'key', NULL, 'financial.cash_session', ARRAY['financial.create']::text[], 'Pay through the caixinha: open/operate a session + create movements.'),
|
|
119
|
+
('financial_cash_review', 'Financeiro', 'Conferência de Caixa', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', ARRAY['financial.read_all']::text[], 'Review/approve closings of other users’ sessions: read_all + edit.'),
|
|
120
|
+
('financial_cash_own', 'Financeiro', 'Caixa Próprio', true, false, true, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Deactivated by 20260803150000 (superseded by financial_cash_receipt/payment); still named in one V1 RPC.'),
|
|
121
|
+
('financial_bank_statement', 'Financeiro', 'Extrato Bancário', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'Bank accounts and statements.'),
|
|
122
|
+
('financial_reconciliation', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'Bank reconciliation. Not seeded by any migration in the V1 repo. plugin-banking-br (openbanking) declares no feature yet; financial.* is the coarser home.'),
|
|
123
|
+
('financial_recurring_bills', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'financial.payables', 'financial.payables.read', '{}'::text[], 'Recurring bills are payables. Not seeded by any migration in the V1 repo.'),
|
|
124
|
+
('financial_ai_expense', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'financial.payables', 'financial.payables.read', '{}'::text[], 'AI expense extraction creates payables. Not seeded by any migration in the V1 repo.'),
|
|
125
|
+
('financial_commissions', 'Financeiro', 'Comissões', true, true, true, 'mapped', 'feature', 'financial.commissions', 'financial.commissions.read', '{}'::text[], 'Commissions.'),
|
|
126
|
+
('financial_credit_card', 'Financeiro', 'Cartões de Crédito', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'Card brands / card fees (plg_financial_card_brands).'),
|
|
127
|
+
('financial_payment', 'Financeiro', 'Pagamento (geral)', true, true, true, 'mapped', 'key', NULL, 'financial.payables.edit', '{}'::text[], 'Settle a payable without a cash session (settlement gate).'),
|
|
128
|
+
('financial_receipt', 'Financeiro', 'Recebimento (geral)', true, true, true, 'mapped', 'key', NULL, 'financial.receivables.edit', '{}'::text[], 'Settle a receivable without a cash session (settlement gate).'),
|
|
129
|
+
('financial_payment_unlink', 'Financeiro', 'Desvincular pagamento de parcela', true, true, true, 'mapped', 'key', NULL, 'financial.edit', '{}'::text[], 'Unlink a payment from an installment; V2 has no finer key — coarser.'),
|
|
130
|
+
('financial_gift_cards', 'Financeiro', 'Gift Cards', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Gift cards have no V2 surface.'),
|
|
131
|
+
('financial_reports', 'Financeiro', 'Extratos e Relatórios', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed; reports_* superseded it.'),
|
|
132
|
+
('financial_free_transaction', 'Financeiro', 'Transacionar sem Caixa', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Deactivated by 20260803150000 (superseded by financial_payment/receipt).'),
|
|
133
|
+
('inventory_dashboard', 'Estoque', 'Estoque (Painel)', true, true, true, 'mapped', 'feature', 'inventory', 'inventory.read', '{}'::text[], 'The stock panel.'),
|
|
134
|
+
('inventory_movements', 'Estoque', 'Movimentações', true, true, true, 'mapped', 'feature', 'inventory', 'inventory.read', '{}'::text[], 'Stock movements (plg_inventory_stock_movements).'),
|
|
135
|
+
('inventory_products', 'Estoque', 'Produtos', true, true, true, 'mapped', 'feature', 'catalog', 'catalog.read', '{}'::text[], 'Products are catalog items in V2 (public.products, PRD 04).'),
|
|
136
|
+
('inventory_categories', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'inventory', 'inventory.read', '{}'::text[], 'plg_inventory_product_categories. Not seeded by any migration in the V1 repo.'),
|
|
137
|
+
('inventory_recipes', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'inventory.recipes', 'inventory.recipes.read', '{}'::text[], 'plugin-inventory declares inventory.recipes when the module is on. Not seeded by any migration in the V1 repo.'),
|
|
138
|
+
('inventory_stock_locations', NULL, NULL, false, NULL, true, 'mapped', 'feature', 'inventory', 'inventory.read', '{}'::text[], 'plg_inventory_stock_locations. Not seeded by any migration in the V1 repo.'),
|
|
139
|
+
('inventory_danfe', 'Estoque', 'Importação DANFE', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; DANFE import has no V2 key (plg_financial_nfe exists without a permission).'),
|
|
140
|
+
('fiscal_dashboard', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (InventorySidebar) but no V2 fiscal surface yet; not seeded by any migration in the V1 repo.'),
|
|
141
|
+
('fiscal_dfe_inbox', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'No V2 fiscal surface; not seeded by any migration in the V1 repo.'),
|
|
142
|
+
('fiscal_dfe_manifest', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'No V2 fiscal surface; not seeded by any migration in the V1 repo.'),
|
|
143
|
+
('fiscal_danfe_import', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'No V2 fiscal surface; not seeded by any migration in the V1 repo.'),
|
|
144
|
+
('fiscal_groups', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'No V2 fiscal surface; not seeded by any migration in the V1 repo.'),
|
|
145
|
+
('production_dashboard', 'Produção', 'Produção (Painel)', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (InventorySidebar) but V2 has no production plugin.'),
|
|
146
|
+
('production_orders', 'Produção', 'Ordens de Produção', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced (only mentioned); no V2 production plugin.'),
|
|
147
|
+
('production_execution', 'Produção', 'Execução (estação)', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; no V2 production plugin.'),
|
|
148
|
+
('production_planning', 'Produção', 'Planejamento e MRP', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; no V2 production plugin.'),
|
|
149
|
+
('production_traceability', 'Produção', 'Lotes e Rastreabilidade', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; no V2 production plugin.'),
|
|
150
|
+
('operations_dashboard', 'Operação', 'Operação', true, true, true, 'mapped', 'feature', 'orders', 'orders.read', '{}'::text[], 'The chef "Operação" module (POS/bills) is orders in V2; coarser.'),
|
|
151
|
+
('restaurant_orders', 'Restaurante', 'Pedidos / Comandas', true, true, true, 'mapped', 'feature', 'orders', 'orders.read', '{}'::text[], 'Bills/comandas are orders (ADR 0004).'),
|
|
152
|
+
('restaurant_menu', 'Restaurante', 'Cardápio', true, true, true, 'mapped', 'feature', 'menu', 'menu.read', '{}'::text[], 'plugin-menu gates on menu.* (referenced feature).'),
|
|
153
|
+
('restaurant_product_prices', NULL, NULL, false, NULL, true, 'mapped', 'key', NULL, 'menu.edit', '{}'::text[], 'Edit menu product prices. Not seeded by any migration in the V1 repo.'),
|
|
154
|
+
('restaurant_product_toggle_active', NULL, NULL, false, NULL, true, 'mapped', 'key', NULL, 'menu.edit', '{}'::text[], 'Toggle a menu product. Not seeded by any migration in the V1 repo.'),
|
|
155
|
+
('restaurant_delivery', 'Restaurante', 'Delivery', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced; delivery has no V2 surface.'),
|
|
156
|
+
('restaurant_kitchen', 'Restaurante', 'Cozinha / Painéis', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: orders.read (kitchen role template already reads orders).'),
|
|
157
|
+
('register_bank_accounts', 'Cadastros', 'Contas Bancárias', true, true, true, 'mapped', 'feature', 'financial', 'financial.read', '{}'::text[], 'plg_financial_bank_accounts.'),
|
|
158
|
+
('register_contacts', 'Cadastros', 'Contatos', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Contacts are people (PRD 03).'),
|
|
159
|
+
('register_employees', 'Cadastros', 'Funcionários', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Employees are people with a role (ADR 0003); membership itself is members.*.'),
|
|
160
|
+
('register_professionals', 'Cadastros', 'Profissionais', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Professionals are people with a role (ADR 0003).'),
|
|
161
|
+
('register_suppliers', 'Cadastros', 'Fornecedores', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Suppliers are people (PRD 03).'),
|
|
162
|
+
('register_partnerships', 'Cadastros', 'Parcerias', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Partners are people of kind partner (PRD 03); coarser.'),
|
|
163
|
+
('register_origins', 'Cadastros', 'Origens', true, true, true, 'mapped', 'feature', 'people', 'people.read', '{}'::text[], 'Client origins are a people attribute registry; coarser.'),
|
|
164
|
+
('register_services', 'Cadastros', 'Serviços', true, true, true, 'mapped', 'feature', 'catalog', 'catalog.read', '{}'::text[], 'Services are catalog items (public.services, PRD 04).'),
|
|
165
|
+
('register_equipment', 'Cadastros', 'Equipamentos', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (Header) but equipment has no V2 surface.'),
|
|
166
|
+
('register_locations', 'Cadastros', 'Locais Operacionais', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (Header). service_locations are inside a unit, an agenda extension in Phase 2 (PRD 02 R1) — not units.'),
|
|
167
|
+
('register_pet_species', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Pet vertical registry; no V2 surface. Not seeded by any migration in the V1 repo.'),
|
|
168
|
+
('sales_quotes', 'Vendas', 'Orçamentos', true, true, true, 'mapped', 'feature', 'crm.quotes', 'crm.quotes.read', '{}'::text[], 'plugin-crm quotes.'),
|
|
169
|
+
('sales_packages', 'Vendas', 'Pacotes de Serviço', true, true, true, 'mapped', 'feature', 'catalog', 'catalog.read', '{}'::text[], 'Service packages are catalog items (kits, PRD 04).'),
|
|
170
|
+
('sales_contracts', 'Vendas', 'Contratos', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (Header) but contracts have no V2 surface.'),
|
|
171
|
+
('service_execution_manual', 'Atendimento', 'Execução manual de serviço', true, true, true, 'mapped', 'key', NULL, 'orders.edit', '{}'::text[], 'Register a service as executed outside the agenda flow: an order-item edit (ADR 0004).'),
|
|
172
|
+
('reports_dashboard', 'Relatórios', 'Relatórios', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'The reports home.'),
|
|
173
|
+
('reports_panels', 'Relatórios', 'Painéis Customizados', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'Custom panels; coarser.'),
|
|
174
|
+
('reports_agenda', 'Relatórios', 'Relatórios de Agenda', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'V2 report cards are owned by the domain plugin and gate on reports.read + the domain’s read; coarser.'),
|
|
175
|
+
('reports_clientes', 'Relatórios', 'Relatórios de Clientes', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
176
|
+
('reports_estoque', 'Relatórios', 'Relatórios de Estoque', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
177
|
+
('reports_financeiro', 'Relatórios', 'Relatórios Financeiros', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda; money reports additionally need financial.read at the card.'),
|
|
178
|
+
('reports_gestao', 'Relatórios', 'Relatórios de Gestão', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
179
|
+
('reports_marketing', 'Relatórios', 'Relatórios de Marketing', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
180
|
+
('reports_profissionais', 'Relatórios', 'Relatórios de Profissionais', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
181
|
+
('reports_servicos', 'Relatórios', 'Relatórios de Serviços', true, true, true, 'mapped', 'feature', 'reports', 'reports.read', '{}'::text[], 'See reports_agenda.'),
|
|
182
|
+
('reports_view', 'Relatórios', 'Relatórios', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Inactive since the 2026-05-12 reseed; reports_* superseded it.'),
|
|
183
|
+
('special_qr_access_issue', 'Ações Especiais', 'Liberar acesso por QR Code', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (Header); QR-code unit access has no V2 surface.'),
|
|
184
|
+
('special_qr_access_required', 'Ações Especiais', 'Exigir liberação por QR Code', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 SQL (user_requires_qr_access); no V2 surface.'),
|
|
185
|
+
('special_time_clock_adjust', 'Ações Especiais', 'Ajustar Registros de Ponto', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (TimeClockRegistry); time clock has no V2 surface.'),
|
|
186
|
+
('special_time_clock_launch_payables', 'Ações Especiais', 'Lançar Ponto em Contas a Pagar', true, true, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (TimeClockRegistry); time clock has no V2 surface.'),
|
|
187
|
+
('special_unmatch_reconciled', NULL, NULL, false, NULL, true, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Enforced in V1 (ReconciliationCard); undo-reconciliation has no V2 key. Not seeded by any migration in the V1 repo.'),
|
|
188
|
+
('special_approve_quote', 'Ações Especiais', 'Aprovar/Rejeitar Orçamento', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: a crm.quotes custom action.'),
|
|
189
|
+
('special_bulk_import', 'Ações Especiais', 'Importação em Massa', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced.'),
|
|
190
|
+
('special_cancel_no_pin', 'Ações Especiais', 'Cancelar Item Sem PIN', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: an orders custom action.'),
|
|
191
|
+
('special_delete_paid_order_item', 'Ações Especiais', 'Excluir Item de Pedido Pago', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: an orders custom action.'),
|
|
192
|
+
('special_discount_no_pin', 'Ações Especiais', 'Aplicar Desconto Sem PIN', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: an orders custom action.'),
|
|
193
|
+
('special_edit_paid_financial', 'Ações Especiais', 'Editar Lançamento com Pagamento', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: a financial custom action.'),
|
|
194
|
+
('special_edit_payment_history', 'Ações Especiais', 'Editar Histórico de Pagamentos', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: a financial custom action.'),
|
|
195
|
+
('special_export_sensitive', 'Ações Especiais', 'Exportar Relatórios Sensíveis', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: a reports custom action.'),
|
|
196
|
+
('special_reopen_closed_bill', 'Ações Especiais', 'Reabrir Comanda Fechada', true, true, false, 'backlog', NULL, NULL, NULL, '{}'::text[], 'Never enforced. Natural target: an orders custom action.'),
|
|
197
|
+
('special_other_user_cash', 'Ações Especiais', 'Acessar Caixa de Outro Usuário', true, false, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Deactivated by 20260803150000; superseded by financial_cash_review / financial.read_all.'),
|
|
198
|
+
('special_super_admin', 'Ações Especiais', 'Acessar Super Admin', true, true, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'Platform super-admin is not a tenant permission in V2 (no bypass in code, R9).'),
|
|
199
|
+
('permissions_enforcement_enabled', 'general_settings', NULL, false, NULL, false, 'dropped', NULL, NULL, NULL, '{}'::text[], 'general_settings flag, not a code: RLS is the law (PRD 02 R4). Never migrates.')
|
|
200
|
+
ON CONFLICT (v1_code) DO UPDATE SET
|
|
201
|
+
v1_category = EXCLUDED.v1_category, v1_name = EXCLUDED.v1_name,
|
|
202
|
+
seeded_in_repo = EXCLUDED.seeded_in_repo, active_in_v1 = EXCLUDED.active_in_v1, enforced_in_v1 = EXCLUDED.enforced_in_v1,
|
|
203
|
+
status = EXCLUDED.status, kind = EXCLUDED.kind, v2_feature = EXCLUDED.v2_feature, v2_key = EXCLUDED.v2_key,
|
|
204
|
+
v2_extra_keys = EXCLUDED.v2_extra_keys, note = EXCLUDED.note, updated_at = now();
|
|
205
|
+
|
|
206
|
+
-- ── every target must exist in the catalog (060 ran first) ──────────────
|
|
207
|
+
-- A mapping onto a key the catalog does not have is a typo, not a decision:
|
|
208
|
+
-- fail the migration rather than let the extractor grant nothing silently.
|
|
209
|
+
DO $$
|
|
210
|
+
DECLARE v_missing text;
|
|
211
|
+
BEGIN
|
|
212
|
+
SELECT string_agg(k, ', ' ORDER BY k) INTO v_missing FROM (
|
|
213
|
+
SELECT m.v2_feature || '.read' AS k FROM app.v1_permission_map m WHERE m.kind = 'feature'
|
|
214
|
+
AND NOT EXISTS (SELECT 1 FROM app.permissions p WHERE p.key = m.v2_feature || '.read')
|
|
215
|
+
UNION
|
|
216
|
+
SELECT unnest(m.v2_extra_keys) FROM app.v1_permission_map m
|
|
217
|
+
EXCEPT
|
|
218
|
+
SELECT p.key FROM app.permissions p
|
|
219
|
+
) s;
|
|
220
|
+
IF v_missing IS NOT NULL THEN
|
|
221
|
+
RAISE EXCEPTION 'v1_permission_map: targets missing from app.permissions: %', v_missing;
|
|
222
|
+
END IF;
|
|
223
|
+
END $$;
|
|
224
|
+
|
|
225
|
+
-- ── the expansion: one V1 rule detail → the V2 keys to grant ────────────
|
|
226
|
+
-- kind='feature': view→F.read, insert→F.create, edit→F.edit, delete→F.delete;
|
|
227
|
+
-- when the action key does not exist for that feature (settings/tenant/units/
|
|
228
|
+
-- roles have read+manage only) the write flags fall back to F.manage.
|
|
229
|
+
-- kind='key': any true flag grants v2_key. Extra keys ride along whenever any
|
|
230
|
+
-- flag is true. Unknown/backlog/dropped codes expand to nothing.
|
|
231
|
+
CREATE OR REPLACE FUNCTION app.v1_permission_keys(
|
|
232
|
+
p_code text, p_view boolean DEFAULT false, p_insert boolean DEFAULT false, p_edit boolean DEFAULT false, p_delete boolean DEFAULT false
|
|
233
|
+
) RETURNS text[]
|
|
234
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = ''
|
|
235
|
+
AS $$
|
|
236
|
+
DECLARE
|
|
237
|
+
m app.v1_permission_map%ROWTYPE;
|
|
238
|
+
out text[] := '{}';
|
|
239
|
+
any_flag boolean := coalesce(p_view, false) OR coalesce(p_insert, false) OR coalesce(p_edit, false) OR coalesce(p_delete, false);
|
|
240
|
+
BEGIN
|
|
241
|
+
SELECT * INTO m FROM app.v1_permission_map WHERE v1_code = p_code;
|
|
242
|
+
IF NOT FOUND OR m.status <> 'mapped' OR NOT any_flag THEN
|
|
243
|
+
RETURN '{}';
|
|
244
|
+
END IF;
|
|
245
|
+
IF m.kind = 'key' THEN
|
|
246
|
+
out := ARRAY[m.v2_key];
|
|
247
|
+
ELSE
|
|
248
|
+
IF coalesce(p_view, false) THEN out := out || (m.v2_feature || '.read'); END IF;
|
|
249
|
+
IF coalesce(p_insert, false) THEN out := out || CASE WHEN EXISTS (SELECT 1 FROM app.permissions WHERE key = m.v2_feature || '.create') THEN m.v2_feature || '.create' ELSE m.v2_feature || '.manage' END; END IF;
|
|
250
|
+
IF coalesce(p_edit, false) THEN out := out || CASE WHEN EXISTS (SELECT 1 FROM app.permissions WHERE key = m.v2_feature || '.edit') THEN m.v2_feature || '.edit' ELSE m.v2_feature || '.manage' END; END IF;
|
|
251
|
+
IF coalesce(p_delete, false) THEN out := out || CASE WHEN EXISTS (SELECT 1 FROM app.permissions WHERE key = m.v2_feature || '.delete') THEN m.v2_feature || '.delete' ELSE m.v2_feature || '.manage' END; END IF;
|
|
252
|
+
END IF;
|
|
253
|
+
out := out || m.v2_extra_keys;
|
|
254
|
+
-- only keys the catalog knows, deduplicated, stable order
|
|
255
|
+
SELECT coalesce(array_agg(DISTINCT k ORDER BY k), '{}') INTO out
|
|
256
|
+
FROM unnest(out) k WHERE EXISTS (SELECT 1 FROM app.permissions p WHERE p.key = k);
|
|
257
|
+
RETURN out;
|
|
258
|
+
END $$;
|
|
259
|
+
REVOKE ALL ON FUNCTION app.v1_permission_keys(text, boolean, boolean, boolean, boolean) FROM public, anon;
|
|
260
|
+
GRANT EXECUTE ON FUNCTION app.v1_permission_keys(text, boolean, boolean, boolean, boolean) TO authenticated, service_role;
|
|
261
|
+
|
|
262
|
+
-- Read view for tooling/UI (security_invoker; the table is readable by any signed-in user).
|
|
263
|
+
CREATE OR REPLACE VIEW public.v_authz_v1_permission_map WITH (security_invoker = true) AS
|
|
264
|
+
SELECT v1_code, v1_category, v1_name, seeded_in_repo, active_in_v1, enforced_in_v1, status, kind, v2_feature, v2_key, v2_extra_keys, note
|
|
265
|
+
FROM app.v1_permission_map;
|
|
266
|
+
GRANT SELECT ON public.v_authz_v1_permission_map TO authenticated, service_role;
|