create-protobase 0.23.13 → 0.24.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/dist/ask-preset.js +25 -0
- package/dist/create-project.js +7 -6
- package/dist/index.js +19 -9
- package/dist/next-steps.js +8 -2
- package/dist/presets.js +17 -0
- package/dist/templates/erp/.build-test-abf2a11f/protobase.config.ts +6 -0
- package/dist/templates/erp/.build-test-abf2a11f/resources.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/index.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/things/data.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/things/ui.ts +2 -0
- package/dist/templates/erp/.env.example +14 -0
- package/dist/templates/erp/AGENTS.md +24 -0
- package/dist/templates/erp/auth/auth.ts +68 -0
- package/dist/templates/erp/components/orders-per-day.tsx +21 -0
- package/dist/templates/erp/config/bottom-bar.ts +9 -0
- package/dist/templates/erp/config/categories/data.ts +20 -0
- package/dist/templates/erp/config/categories/ui.ts +11 -0
- package/dist/templates/erp/config/companies/data.ts +23 -0
- package/dist/templates/erp/config/companies/ui.ts +22 -0
- package/dist/templates/erp/config/company_tags/data.ts +16 -0
- package/dist/templates/erp/config/company_tags/ui.ts +7 -0
- package/dist/templates/erp/config/countries/data.ts +13 -0
- package/dist/templates/erp/config/countries/ui.ts +6 -0
- package/dist/templates/erp/config/currencies/data.ts +14 -0
- package/dist/templates/erp/config/currencies/ui.ts +6 -0
- package/dist/templates/erp/config/emp_master/data.ts +19 -0
- package/dist/templates/erp/config/emp_master/ui.ts +7 -0
- package/dist/templates/erp/config/index.ts +42 -0
- package/dist/templates/erp/config/invoice_lines/data.ts +23 -0
- package/dist/templates/erp/config/invoice_lines/ui.ts +7 -0
- package/dist/templates/erp/config/invoices/data.ts +26 -0
- package/dist/templates/erp/config/invoices/ui.ts +38 -0
- package/dist/templates/erp/config/jobs.test.ts +23 -0
- package/dist/templates/erp/config/jobs.ts +29 -0
- package/dist/templates/erp/config/locations/data.ts +18 -0
- package/dist/templates/erp/config/locations/ui.ts +6 -0
- package/dist/templates/erp/config/order_lines/data.ts +20 -0
- package/dist/templates/erp/config/order_lines/ui.ts +7 -0
- package/dist/templates/erp/config/orders/data.ts +27 -0
- package/dist/templates/erp/config/orders/ui.ts +41 -0
- package/dist/templates/erp/config/organizations/data.ts +17 -0
- package/dist/templates/erp/config/organizations/ui.ts +6 -0
- package/dist/templates/erp/config/overview/page.tsx +80 -0
- package/dist/templates/erp/config/pages.test.ts +14 -0
- package/dist/templates/erp/config/people/data.ts +21 -0
- package/dist/templates/erp/config/people/ui.ts +6 -0
- package/dist/templates/erp/config/products/data.ts +25 -0
- package/dist/templates/erp/config/products/ui.ts +6 -0
- package/dist/templates/erp/config/roles.test.ts +56 -0
- package/dist/templates/erp/config/roles.ts +54 -0
- package/dist/templates/erp/config/shell.test.ts +24 -0
- package/dist/templates/erp/config/stock_levels/data.ts +16 -0
- package/dist/templates/erp/config/stock_levels/ui.ts +6 -0
- package/dist/templates/erp/config/stock_moves/data.ts +22 -0
- package/dist/templates/erp/config/stock_moves/ui.ts +25 -0
- package/dist/templates/erp/config/tags/data.ts +17 -0
- package/dist/templates/erp/config/tags/ui.ts +6 -0
- package/dist/templates/erp/config/user-menu.ts +9 -0
- package/dist/templates/erp/config/users/data.ts +19 -0
- package/dist/templates/erp/config/users/ui.ts +9 -0
- package/dist/templates/erp/config/warehouses/data.ts +19 -0
- package/dist/templates/erp/config/warehouses/ui.ts +6 -0
- package/dist/templates/erp/data.ts +3 -0
- package/dist/templates/erp/db/connection.ts +7 -0
- package/dist/templates/erp/db/drop.ts +9 -0
- package/dist/templates/erp/db/kysely.ts +13 -0
- package/dist/templates/erp/db/migrations/001_core.sql +48 -0
- package/dist/templates/erp/db/migrations/002_crm.sql +59 -0
- package/dist/templates/erp/db/migrations/003_catalog.sql +38 -0
- package/dist/templates/erp/db/migrations/004_sales.sql +86 -0
- package/dist/templates/erp/db/migrations/005_inventory.sql +65 -0
- package/dist/templates/erp/db/migrations/006_hr_legacy.sql +16 -0
- package/dist/templates/erp/db/migrations/007_filter_indexes.sql +4 -0
- package/dist/templates/erp/db/migrations/008_calculated_totals.sql +98 -0
- package/dist/templates/erp/db/migrations/011_auth_organizations.sql +22 -0
- package/dist/templates/erp/db/migrations/012_jobs.sql +531 -0
- package/dist/templates/erp/db/migrations/013_jobs.sql +98 -0
- package/dist/templates/erp/db/migrations/016_jobs.sql +99 -0
- package/dist/templates/erp/db/migrations/017_filter_indexes.sql +5 -0
- package/dist/templates/erp/db/migrations/018_sort_indexes.sql +4 -0
- package/dist/templates/erp/db/migrations/019_auth_users.sql +18 -0
- package/dist/templates/erp/functions/unpaid-orders.ts +13 -0
- package/dist/templates/erp/package.json +38 -0
- package/dist/templates/erp/protobase.config.ts +22 -0
- package/dist/templates/erp/protobase.ui.tsx +14 -0
- package/dist/templates/erp/seed/auth-organizations.ts +11 -0
- package/dist/templates/erp/seed/base-organization.test.ts +17 -0
- package/dist/templates/erp/seed/base-organization.ts +10 -0
- package/dist/templates/erp/seed/base.ts +22 -0
- package/dist/templates/erp/seed/builders/categories.ts +18 -0
- package/dist/templates/erp/seed/builders/company.ts +61 -0
- package/dist/templates/erp/seed/builders/invoice.ts +31 -0
- package/dist/templates/erp/seed/builders/order.ts +66 -0
- package/dist/templates/erp/seed/builders/product.ts +46 -0
- package/dist/templates/erp/seed/calendar.ts +60 -0
- package/dist/templates/erp/seed/cli.ts +54 -0
- package/dist/templates/erp/seed/copy.ts +22 -0
- package/dist/templates/erp/seed/data/catalogTree.ts +72 -0
- package/dist/templates/erp/seed/data/layout.ts +19 -0
- package/dist/templates/erp/seed/data/names.ts +44 -0
- package/dist/templates/erp/seed/data/reference.ts +53 -0
- package/dist/templates/erp/seed/format.ts +24 -0
- package/dist/templates/erp/seed/maintenance.ts +63 -0
- package/dist/templates/erp/seed/rng.ts +36 -0
- package/dist/templates/erp/seed/scales.ts +9 -0
- package/dist/templates/erp/seed/steps/catalog.ts +34 -0
- package/dist/templates/erp/seed/steps/crm.ts +62 -0
- package/dist/templates/erp/seed/steps/hr.ts +26 -0
- package/dist/templates/erp/seed/steps/ledger.ts +88 -0
- package/dist/templates/erp/seed/steps/reference.ts +24 -0
- package/dist/templates/erp/seed/steps/sales.ts +58 -0
- package/dist/templates/erp/seed/steps/warehouses.ts +42 -0
- package/dist/templates/erp/seed/world.ts +86 -0
- package/dist/templates/erp/vitest.config.ts +4 -0
- package/dist/templates/real-estate/.env.example +9 -0
- package/dist/templates/real-estate/AGENTS.md +24 -0
- package/dist/templates/real-estate/_.gitignore +3 -0
- package/dist/templates/real-estate/_bunfig.toml +4 -0
- package/dist/templates/real-estate/auth/auth.ts +36 -0
- package/dist/templates/real-estate/config/amenities/data.ts +12 -0
- package/dist/templates/real-estate/config/amenities/ui.ts +7 -0
- package/dist/templates/real-estate/config/arrears/data.ts +19 -0
- package/dist/templates/real-estate/config/arrears/ui.ts +21 -0
- package/dist/templates/real-estate/config/deposits/data.ts +21 -0
- package/dist/templates/real-estate/config/deposits/ui.ts +17 -0
- package/dist/templates/real-estate/config/index.ts +41 -0
- package/dist/templates/real-estate/config/inspections/data.ts +22 -0
- package/dist/templates/real-estate/config/inspections/ui.ts +15 -0
- package/dist/templates/real-estate/config/lease_tenants/data.ts +16 -0
- package/dist/templates/real-estate/config/lease_tenants/ui.ts +8 -0
- package/dist/templates/real-estate/config/leases/data.ts +29 -0
- package/dist/templates/real-estate/config/leases/ui.ts +33 -0
- package/dist/templates/real-estate/config/organizations/data.ts +17 -0
- package/dist/templates/real-estate/config/organizations/ui.ts +7 -0
- package/dist/templates/real-estate/config/overview/page.tsx +57 -0
- package/dist/templates/real-estate/config/owners/data.ts +22 -0
- package/dist/templates/real-estate/config/owners/ui.ts +17 -0
- package/dist/templates/real-estate/config/pages.test.ts +49 -0
- package/dist/templates/real-estate/config/payments/data.ts +20 -0
- package/dist/templates/real-estate/config/payments/ui.ts +14 -0
- package/dist/templates/real-estate/config/properties/data.ts +26 -0
- package/dist/templates/real-estate/config/properties/ui.ts +21 -0
- package/dist/templates/real-estate/config/rent_charges/data.ts +22 -0
- package/dist/templates/real-estate/config/rent_charges/ui.ts +27 -0
- package/dist/templates/real-estate/config/roles.test.ts +49 -0
- package/dist/templates/real-estate/config/roles.ts +45 -0
- package/dist/templates/real-estate/config/shell.test.ts +18 -0
- package/dist/templates/real-estate/config/tenants/data.ts +23 -0
- package/dist/templates/real-estate/config/tenants/ui.ts +27 -0
- package/dist/templates/real-estate/config/tickets/data.ts +33 -0
- package/dist/templates/real-estate/config/tickets/ui.ts +58 -0
- package/dist/templates/real-estate/config/unit_amenities/data.ts +15 -0
- package/dist/templates/real-estate/config/unit_amenities/ui.ts +8 -0
- package/dist/templates/real-estate/config/units/data.ts +24 -0
- package/dist/templates/real-estate/config/units/ui.ts +41 -0
- package/dist/templates/real-estate/config/user-menu.ts +9 -0
- package/dist/templates/real-estate/config/users/data.ts +19 -0
- package/dist/templates/real-estate/config/users/ui.ts +11 -0
- package/dist/templates/real-estate/config/valuations/data.ts +19 -0
- package/dist/templates/real-estate/config/valuations/ui.ts +13 -0
- package/dist/templates/real-estate/config/vendors/data.ts +21 -0
- package/dist/templates/real-estate/config/vendors/ui.ts +18 -0
- package/dist/templates/real-estate/config/work_orders/data.ts +22 -0
- package/dist/templates/real-estate/config/work_orders/ui.ts +18 -0
- package/dist/templates/real-estate/db/connection.ts +7 -0
- package/dist/templates/real-estate/db/drop.ts +9 -0
- package/dist/templates/real-estate/db/kysely.ts +13 -0
- package/dist/templates/real-estate/db/migrate.ts +30 -0
- package/dist/templates/real-estate/db/migrations/001_core.sql +33 -0
- package/dist/templates/real-estate/db/migrations/002_portfolio.sql +97 -0
- package/dist/templates/real-estate/db/migrations/003_leasing.sql +78 -0
- package/dist/templates/real-estate/db/migrations/004_billing.sql +88 -0
- package/dist/templates/real-estate/db/migrations/005_maintenance.sql +105 -0
- package/dist/templates/real-estate/db/migrations/006_outstanding.sql +14 -0
- package/dist/templates/real-estate/db/migrations/007_auth.sql +36 -0
- package/dist/templates/real-estate/db/migrations/008_auth_snake_case.sql +109 -0
- package/dist/templates/real-estate/db/migrations/009_auth.sql +6 -0
- package/dist/templates/real-estate/db/migrations/010_auth.sql +4 -0
- package/dist/templates/real-estate/db/migrations/011_filter_indexes.sql +8 -0
- package/dist/templates/real-estate/db/migrations/012_sort_indexes.sql +2 -0
- package/dist/templates/real-estate/db/migrations/013_auth_users.sql +17 -0
- package/dist/templates/real-estate/node-types.d.ts +1 -0
- package/dist/templates/real-estate/package.json +38 -0
- package/dist/templates/real-estate/protobase.config.ts +5 -0
- package/dist/templates/real-estate/protobase.ui.tsx +16 -0
- package/dist/templates/real-estate/seed/base-organization.test.ts +17 -0
- package/dist/templates/real-estate/seed/base-organization.ts +10 -0
- package/dist/templates/real-estate/seed/base.ts +18 -0
- package/dist/templates/real-estate/seed/builders/allocation.ts +47 -0
- package/dist/templates/real-estate/seed/builders/ledger.ts +52 -0
- package/dist/templates/real-estate/seed/builders/property.ts +98 -0
- package/dist/templates/real-estate/seed/builders/tenancy.ts +99 -0
- package/dist/templates/real-estate/seed/builders/ticket.ts +115 -0
- package/dist/templates/real-estate/seed/calendar.ts +77 -0
- package/dist/templates/real-estate/seed/cli.ts +50 -0
- package/dist/templates/real-estate/seed/copy.ts +22 -0
- package/dist/templates/real-estate/seed/data/names.ts +31 -0
- package/dist/templates/real-estate/seed/data/reference.ts +63 -0
- package/dist/templates/real-estate/seed/format.ts +24 -0
- package/dist/templates/real-estate/seed/iban.ts +15 -0
- package/dist/templates/real-estate/seed/maintenance.ts +63 -0
- package/dist/templates/real-estate/seed/rng.ts +36 -0
- package/dist/templates/real-estate/seed/scales.ts +9 -0
- package/dist/templates/real-estate/seed/steps/billing.ts +44 -0
- package/dist/templates/real-estate/seed/steps/leasing.ts +60 -0
- package/dist/templates/real-estate/seed/steps/maintenance.ts +77 -0
- package/dist/templates/real-estate/seed/steps/portfolio.ts +97 -0
- package/dist/templates/real-estate/seed/steps/reference.ts +21 -0
- package/dist/templates/real-estate/seed/world.ts +92 -0
- package/dist/templates/real-estate/tsconfig.json +16 -0
- package/dist/templates/real-estate/vitest.config.ts +4 -0
- package/dist/templates/scratch/_.gitignore +3 -0
- package/dist/templates/scratch/_bunfig.toml +4 -0
- package/dist/templates/scratch/db/migrate.ts +30 -0
- package/dist/templates/scratch/db/migrations/001_auth.sql +36 -0
- package/dist/templates/scratch/db/migrations/002_auth_snake_case.sql +109 -0
- package/dist/templates/scratch/db/migrations/003_auth.sql +6 -0
- package/dist/templates/scratch/db/migrations/004_auth.sql +4 -0
- package/dist/templates/scratch/node-types.d.ts +1 -0
- package/dist/{template → templates/scratch}/package.json +5 -5
- package/dist/templates/scratch/tsconfig.json +16 -0
- package/package.json +2 -2
- /package/dist/{template → templates/erp}/_.gitignore +0 -0
- /package/dist/{template → templates/erp}/_bunfig.toml +0 -0
- /package/dist/{template → templates/erp}/db/migrate.ts +0 -0
- /package/dist/{template/db/migrations/001_auth.sql → templates/erp/db/migrations/009_auth.sql} +0 -0
- /package/dist/{template/db/migrations/002_auth_snake_case.sql → templates/erp/db/migrations/010_auth_snake_case.sql} +0 -0
- /package/dist/{template/db/migrations/003_auth.sql → templates/erp/db/migrations/014_auth.sql} +0 -0
- /package/dist/{template/db/migrations/004_auth.sql → templates/erp/db/migrations/015_auth.sql} +0 -0
- /package/dist/{template → templates/erp}/node-types.d.ts +0 -0
- /package/dist/{template → templates/erp}/tsconfig.json +0 -0
- /package/dist/{template → templates/scratch}/.env.example +0 -0
- /package/dist/{template → templates/scratch}/AGENTS.md +0 -0
- /package/dist/{template → templates/scratch}/auth/auth.ts +0 -0
- /package/dist/{template → templates/scratch}/config/index.ts +0 -0
- /package/dist/{template → templates/scratch}/config/user-menu.ts +0 -0
- /package/dist/{template → templates/scratch}/db/connection.ts +0 -0
- /package/dist/{template → templates/scratch}/db/migrations/.gitkeep +0 -0
- /package/dist/{template → templates/scratch}/protobase.config.ts +0 -0
- /package/dist/{template → templates/scratch}/vitest.config.ts +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { view } from '@protobase/schema'
|
|
2
|
+
import type { users } from './data'
|
|
3
|
+
|
|
4
|
+
export const usersView = view<typeof users>('users')
|
|
5
|
+
.names({ singular: 'User', plural: 'Users' })
|
|
6
|
+
// Reached from the records that name a user; the profile menu's Users is the accounts page.
|
|
7
|
+
.nav({ hidden: true })
|
|
8
|
+
.fields((r) => ({ role: r.role.valueLabels({ admin: 'Admin', sales: 'Sales', finance: 'Finance', warehouse: 'Warehouse' }) }))
|
|
9
|
+
.list((r) => ({ columns: [r.email, r.name, r.role, r.active] }))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { f, resource } from '@protobase/schema'
|
|
2
|
+
import { erpAccess } from '../roles'
|
|
3
|
+
|
|
4
|
+
/** inventory.warehouses. Unique: (organization_id, code) */
|
|
5
|
+
export const warehouses = resource('warehouses')
|
|
6
|
+
.table('inventory.warehouses')
|
|
7
|
+
.fields({
|
|
8
|
+
id: f.integer().readOnly().filterable().sortable().dbDefault(),
|
|
9
|
+
organization_id: f.relation('organizations').filterable().sortable(),
|
|
10
|
+
code: f.text().filterable().sortable(),
|
|
11
|
+
name: f.text(),
|
|
12
|
+
city: f.text(),
|
|
13
|
+
country_code: f.relation('countries').filterable().sortable(),
|
|
14
|
+
created_at: f.timestamp().readOnly().dbDefault(),
|
|
15
|
+
updated_at: f.timestamp().readOnly().dbDefault(),
|
|
16
|
+
})
|
|
17
|
+
.primaryKey((r) => r.id)
|
|
18
|
+
.tenant((r) => r.organization_id)
|
|
19
|
+
.access(erpAccess('warehouses'))
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import postgres from 'postgres'
|
|
2
|
+
|
|
3
|
+
export const databaseUrl = process.env.DATABASE_URL ?? 'postgres://protobase:protobase@localhost:55432/protobase'
|
|
4
|
+
|
|
5
|
+
export const connect = (options: postgres.Options<{}> = {}) => postgres(databaseUrl, { onnotice: () => {}, ...options })
|
|
6
|
+
|
|
7
|
+
export type Db = ReturnType<typeof connect>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { connect } from './connection'
|
|
2
|
+
|
|
3
|
+
const sql = connect({ max: 1 })
|
|
4
|
+
|
|
5
|
+
await sql.unsafe('drop schema if exists core, crm, catalog, sales, inventory, hr cascade')
|
|
6
|
+
await sql.unsafe('drop table if exists public.schema_migrations, public.seed_info')
|
|
7
|
+
console.log('dropped all erp schemas')
|
|
8
|
+
|
|
9
|
+
await sql.end()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Kysely, PostgresDialect } from 'kysely'
|
|
2
|
+
import pg from 'pg'
|
|
3
|
+
import { databaseUrl } from './connection'
|
|
4
|
+
|
|
5
|
+
// date and timestamp (without zone) stay text; the API formats them itself
|
|
6
|
+
const types = {
|
|
7
|
+
getTypeParser: (oid: number, format?: 'text' | 'binary') =>
|
|
8
|
+
oid === 1082 || oid === 1114 ? (value: string) => value : pg.types.getTypeParser(oid, format as 'text'),
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Kysely over node-postgres, the way a deployment of the API would connect. */
|
|
12
|
+
export const createDb = (max = 10) =>
|
|
13
|
+
new Kysely<any>({ dialect: new PostgresDialect({ pool: new pg.Pool({ connectionString: databaseUrl, max, types }) }) })
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
create extension if not exists pg_trgm;
|
|
2
|
+
create extension if not exists ltree;
|
|
3
|
+
|
|
4
|
+
create table public.seed_info (
|
|
5
|
+
scale text not null,
|
|
6
|
+
seeded_at timestamptz not null default now()
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
create schema core;
|
|
10
|
+
|
|
11
|
+
create table core.countries (
|
|
12
|
+
code text primary key,
|
|
13
|
+
name text not null,
|
|
14
|
+
eu_member boolean not null default false
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
create table core.currencies (
|
|
18
|
+
code text primary key,
|
|
19
|
+
name text not null,
|
|
20
|
+
symbol text not null,
|
|
21
|
+
decimals smallint not null default 2
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
create table core.organizations (
|
|
25
|
+
id integer generated by default as identity primary key,
|
|
26
|
+
name text not null,
|
|
27
|
+
slug text not null unique,
|
|
28
|
+
country_code text not null references core.countries (code),
|
|
29
|
+
currency_code text not null references core.currencies (code),
|
|
30
|
+
created_at timestamptz not null default now(),
|
|
31
|
+
updated_at timestamptz not null default now()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
create table core.users (
|
|
35
|
+
id integer generated by default as identity primary key,
|
|
36
|
+
organization_id integer not null references core.organizations (id),
|
|
37
|
+
email text not null,
|
|
38
|
+
name text not null,
|
|
39
|
+
role text not null check (role in ('admin', 'sales', 'finance', 'warehouse')),
|
|
40
|
+
active boolean not null default true,
|
|
41
|
+
created_at timestamptz not null default now(),
|
|
42
|
+
updated_at timestamptz not null default now(),
|
|
43
|
+
unique (organization_id, email)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
create index users_role_idx on core.users (organization_id, role);
|
|
47
|
+
create index organizations_country_idx on core.organizations (country_code);
|
|
48
|
+
create index organizations_currency_idx on core.organizations (currency_code);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
create schema crm;
|
|
2
|
+
|
|
3
|
+
create table crm.companies (
|
|
4
|
+
id bigint generated by default as identity primary key,
|
|
5
|
+
organization_id integer not null references core.organizations (id),
|
|
6
|
+
name text not null,
|
|
7
|
+
country_code text not null references core.countries (code),
|
|
8
|
+
city text not null,
|
|
9
|
+
vat_number text,
|
|
10
|
+
email text,
|
|
11
|
+
phone text,
|
|
12
|
+
status text not null default 'active' check (status in ('lead', 'active', 'dormant')),
|
|
13
|
+
created_at timestamptz not null default now(),
|
|
14
|
+
updated_at timestamptz not null default now()
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
create index companies_organization_idx on crm.companies (organization_id, created_at);
|
|
18
|
+
create index companies_country_idx on crm.companies (country_code);
|
|
19
|
+
create index companies_status_idx on crm.companies (organization_id, status);
|
|
20
|
+
create index companies_name_trgm_idx on crm.companies using gin (name gin_trgm_ops);
|
|
21
|
+
|
|
22
|
+
create table crm.people (
|
|
23
|
+
id bigint generated by default as identity primary key,
|
|
24
|
+
organization_id integer not null references core.organizations (id),
|
|
25
|
+
company_id bigint not null references crm.companies (id),
|
|
26
|
+
first_name text not null,
|
|
27
|
+
last_name text not null,
|
|
28
|
+
email text,
|
|
29
|
+
phone text,
|
|
30
|
+
job_title text,
|
|
31
|
+
created_at timestamptz not null default now(),
|
|
32
|
+
updated_at timestamptz not null default now()
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
create index people_company_idx on crm.people (company_id);
|
|
36
|
+
create index people_organization_idx on crm.people (organization_id, created_at);
|
|
37
|
+
|
|
38
|
+
create table crm.tags (
|
|
39
|
+
id integer generated by default as identity primary key,
|
|
40
|
+
organization_id integer not null references core.organizations (id),
|
|
41
|
+
name text not null,
|
|
42
|
+
color text not null,
|
|
43
|
+
created_at timestamptz not null default now(),
|
|
44
|
+
updated_at timestamptz not null default now(),
|
|
45
|
+
unique (organization_id, name)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
create table crm.company_tags (
|
|
49
|
+
company_id bigint not null references crm.companies (id),
|
|
50
|
+
tag_id integer not null references crm.tags (id),
|
|
51
|
+
organization_id integer not null references core.organizations (id),
|
|
52
|
+
added_by integer references core.users (id),
|
|
53
|
+
added_at timestamptz not null default now(),
|
|
54
|
+
primary key (company_id, tag_id)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
create index company_tags_tag_idx on crm.company_tags (tag_id);
|
|
58
|
+
create index company_tags_organization_idx on crm.company_tags (organization_id);
|
|
59
|
+
create index company_tags_added_by_idx on crm.company_tags (added_by);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
create schema catalog;
|
|
2
|
+
|
|
3
|
+
create table catalog.categories (
|
|
4
|
+
id integer generated by default as identity primary key,
|
|
5
|
+
organization_id integer not null references core.organizations (id),
|
|
6
|
+
parent_id integer references catalog.categories (id),
|
|
7
|
+
name text not null,
|
|
8
|
+
slug text not null,
|
|
9
|
+
position integer not null default 0,
|
|
10
|
+
created_at timestamptz not null default now(),
|
|
11
|
+
updated_at timestamptz not null default now(),
|
|
12
|
+
unique (organization_id, slug)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
create index categories_parent_idx on catalog.categories (parent_id, position);
|
|
16
|
+
|
|
17
|
+
create table catalog.products (
|
|
18
|
+
id bigint generated by default as identity primary key,
|
|
19
|
+
organization_id integer not null references core.organizations (id),
|
|
20
|
+
category_id integer not null references catalog.categories (id),
|
|
21
|
+
sku text not null,
|
|
22
|
+
name text not null,
|
|
23
|
+
description text,
|
|
24
|
+
price numeric(12, 2) not null check (price >= 0),
|
|
25
|
+
currency_code text not null references core.currencies (code),
|
|
26
|
+
attributes jsonb not null default '{}',
|
|
27
|
+
active boolean not null default true,
|
|
28
|
+
deleted_at timestamptz,
|
|
29
|
+
created_at timestamptz not null default now(),
|
|
30
|
+
updated_at timestamptz not null default now(),
|
|
31
|
+
unique (organization_id, sku)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
create index products_category_idx on catalog.products (category_id);
|
|
35
|
+
create index products_organization_idx on catalog.products (organization_id, created_at);
|
|
36
|
+
create index products_name_trgm_idx on catalog.products using gin (name gin_trgm_ops);
|
|
37
|
+
create index products_attributes_idx on catalog.products using gin (attributes jsonb_path_ops);
|
|
38
|
+
create index products_live_idx on catalog.products (organization_id) where deleted_at is null;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
create schema sales;
|
|
2
|
+
|
|
3
|
+
create type sales.invoice_status as enum ('draft', 'sent', 'paid', 'overdue');
|
|
4
|
+
|
|
5
|
+
create table sales.orders (
|
|
6
|
+
id uuid primary key default uuidv7(),
|
|
7
|
+
organization_id integer not null references core.organizations (id),
|
|
8
|
+
number text not null,
|
|
9
|
+
company_id bigint not null references crm.companies (id),
|
|
10
|
+
person_id bigint references crm.people (id),
|
|
11
|
+
owner_id integer not null references core.users (id),
|
|
12
|
+
status text not null check (status in ('draft', 'confirmed', 'picking', 'shipped', 'delivered', 'cancelled')),
|
|
13
|
+
currency_code text not null references core.currencies (code),
|
|
14
|
+
discount_percent numeric(5, 2) not null default 0,
|
|
15
|
+
total numeric(14, 2) not null default 0,
|
|
16
|
+
paid boolean not null default false,
|
|
17
|
+
notes text,
|
|
18
|
+
created_at timestamptz not null default now(),
|
|
19
|
+
updated_at timestamptz not null default now(),
|
|
20
|
+
unique (organization_id, number)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
create index orders_organization_idx on sales.orders (organization_id, created_at);
|
|
24
|
+
create index orders_company_idx on sales.orders (company_id);
|
|
25
|
+
create index orders_person_idx on sales.orders (person_id);
|
|
26
|
+
create index orders_owner_idx on sales.orders (owner_id);
|
|
27
|
+
create index orders_status_idx on sales.orders (organization_id, status);
|
|
28
|
+
create index orders_created_idx on sales.orders (created_at);
|
|
29
|
+
|
|
30
|
+
create table sales.order_lines (
|
|
31
|
+
order_id uuid not null references sales.orders (id),
|
|
32
|
+
line_no integer not null,
|
|
33
|
+
organization_id integer not null references core.organizations (id),
|
|
34
|
+
product_id bigint not null references catalog.products (id),
|
|
35
|
+
description text not null,
|
|
36
|
+
quantity integer not null check (quantity > 0),
|
|
37
|
+
unit_price numeric(12, 2) not null,
|
|
38
|
+
created_at timestamptz not null default now(),
|
|
39
|
+
updated_at timestamptz not null default now(),
|
|
40
|
+
primary key (order_id, line_no)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
create index order_lines_product_idx on sales.order_lines (product_id);
|
|
44
|
+
create index order_lines_organization_idx on sales.order_lines (organization_id);
|
|
45
|
+
|
|
46
|
+
create table sales.invoices (
|
|
47
|
+
id bigint generated by default as identity primary key,
|
|
48
|
+
organization_id integer not null references core.organizations (id),
|
|
49
|
+
number text not null,
|
|
50
|
+
order_id uuid references sales.orders (id),
|
|
51
|
+
company_id bigint not null references crm.companies (id),
|
|
52
|
+
status sales.invoice_status not null default 'draft',
|
|
53
|
+
issued_at date not null,
|
|
54
|
+
due_at date not null,
|
|
55
|
+
vat_rate numeric(4, 2) not null default 21,
|
|
56
|
+
subtotal numeric(14, 2) not null,
|
|
57
|
+
vat numeric(14, 2) not null,
|
|
58
|
+
total numeric(14, 2) not null,
|
|
59
|
+
created_at timestamptz not null default now(),
|
|
60
|
+
updated_at timestamptz not null default now(),
|
|
61
|
+
unique (organization_id, number)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
create index invoices_organization_idx on sales.invoices (organization_id, created_at);
|
|
65
|
+
create index invoices_company_idx on sales.invoices (company_id);
|
|
66
|
+
create index invoices_order_idx on sales.invoices (order_id);
|
|
67
|
+
create index invoices_status_idx on sales.invoices (organization_id, status);
|
|
68
|
+
create index invoices_due_idx on sales.invoices (due_at) where status in ('sent', 'overdue');
|
|
69
|
+
|
|
70
|
+
create table sales.invoice_lines (
|
|
71
|
+
id bigint generated by default as identity primary key,
|
|
72
|
+
invoice_id bigint not null references sales.invoices (id),
|
|
73
|
+
organization_id integer not null references core.organizations (id),
|
|
74
|
+
position integer not null,
|
|
75
|
+
product_id bigint references catalog.products (id),
|
|
76
|
+
description text not null,
|
|
77
|
+
quantity integer not null check (quantity > 0),
|
|
78
|
+
unit_price numeric(12, 2) not null,
|
|
79
|
+
line_total numeric(14, 2) generated always as (quantity * unit_price) stored,
|
|
80
|
+
created_at timestamptz not null default now(),
|
|
81
|
+
updated_at timestamptz not null default now(),
|
|
82
|
+
unique (invoice_id, position)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
create index invoice_lines_product_idx on sales.invoice_lines (product_id);
|
|
86
|
+
create index invoice_lines_organization_idx on sales.invoice_lines (organization_id);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
create schema inventory;
|
|
2
|
+
|
|
3
|
+
create table inventory.warehouses (
|
|
4
|
+
id integer generated by default as identity primary key,
|
|
5
|
+
organization_id integer not null references core.organizations (id),
|
|
6
|
+
code text not null,
|
|
7
|
+
name text not null,
|
|
8
|
+
city text not null,
|
|
9
|
+
country_code text not null references core.countries (code),
|
|
10
|
+
created_at timestamptz not null default now(),
|
|
11
|
+
updated_at timestamptz not null default now(),
|
|
12
|
+
unique (organization_id, code)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
create index warehouses_country_idx on inventory.warehouses (country_code);
|
|
16
|
+
|
|
17
|
+
create table inventory.locations (
|
|
18
|
+
id integer generated by default as identity primary key,
|
|
19
|
+
organization_id integer not null references core.organizations (id),
|
|
20
|
+
warehouse_id integer not null references inventory.warehouses (id),
|
|
21
|
+
path ltree not null,
|
|
22
|
+
name text not null,
|
|
23
|
+
created_at timestamptz not null default now(),
|
|
24
|
+
updated_at timestamptz not null default now(),
|
|
25
|
+
unique (warehouse_id, path)
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
create index locations_organization_idx on inventory.locations (organization_id);
|
|
29
|
+
create index locations_path_idx on inventory.locations using gist (path);
|
|
30
|
+
|
|
31
|
+
-- Append-only ledger: rows are never updated, so there is no updated_at.
|
|
32
|
+
create table inventory.stock_moves (
|
|
33
|
+
id bigint generated by default as identity primary key,
|
|
34
|
+
organization_id integer not null references core.organizations (id),
|
|
35
|
+
product_id bigint not null references catalog.products (id),
|
|
36
|
+
warehouse_id integer not null references inventory.warehouses (id),
|
|
37
|
+
location_id integer not null references inventory.locations (id),
|
|
38
|
+
kind text not null check (kind in ('receipt', 'issue', 'transfer', 'adjustment')),
|
|
39
|
+
quantity integer not null check (quantity <> 0),
|
|
40
|
+
unit_cost numeric(12, 2) not null,
|
|
41
|
+
reference text,
|
|
42
|
+
moved_at timestamptz not null,
|
|
43
|
+
user_id integer references core.users (id)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
create index stock_moves_moved_at_idx on inventory.stock_moves (moved_at);
|
|
47
|
+
create index stock_moves_organization_idx on inventory.stock_moves (organization_id, moved_at);
|
|
48
|
+
create index stock_moves_product_idx on inventory.stock_moves (product_id, moved_at);
|
|
49
|
+
create index stock_moves_warehouse_idx on inventory.stock_moves (warehouse_id);
|
|
50
|
+
create index stock_moves_location_idx on inventory.stock_moves (location_id);
|
|
51
|
+
create index stock_moves_user_idx on inventory.stock_moves (user_id);
|
|
52
|
+
create index stock_moves_kind_idx on inventory.stock_moves (kind);
|
|
53
|
+
|
|
54
|
+
-- Derived from stock_moves: on-hand quantity per product and location.
|
|
55
|
+
create table inventory.stock_levels (
|
|
56
|
+
product_id bigint not null references catalog.products (id),
|
|
57
|
+
location_id integer not null references inventory.locations (id),
|
|
58
|
+
organization_id integer not null references core.organizations (id),
|
|
59
|
+
quantity integer not null,
|
|
60
|
+
updated_at timestamptz not null default now(),
|
|
61
|
+
primary key (product_id, location_id)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
create index stock_levels_location_idx on inventory.stock_levels (location_id);
|
|
65
|
+
create index stock_levels_organization_idx on inventory.stock_levels (organization_id);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
create schema hr;
|
|
2
|
+
|
|
3
|
+
-- Legacy payroll import: uppercase identifiers, kept as-is on purpose.
|
|
4
|
+
create table hr."EMP_MASTER" (
|
|
5
|
+
"EMP_ID" integer primary key,
|
|
6
|
+
"ORGANIZATION_ID" integer not null references core.organizations (id),
|
|
7
|
+
"EMP_NAME" varchar(60) not null,
|
|
8
|
+
"DEPT_CD" char(3) not null,
|
|
9
|
+
"HIRE_DT" date not null,
|
|
10
|
+
"SAL_AMT" numeric(10, 2),
|
|
11
|
+
"MGR_ID" integer references hr."EMP_MASTER" ("EMP_ID"),
|
|
12
|
+
"ACTIVE_FLG" char(1) not null default 'Y' check ("ACTIVE_FLG" in ('Y', 'N'))
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
create index "EMP_MASTER_ORG_IDX" on hr."EMP_MASTER" ("ORGANIZATION_ID");
|
|
16
|
+
create index "EMP_MASTER_MGR_IDX" on hr."EMP_MASTER" ("MGR_ID");
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
create index orders_total_idx on sales.orders (organization_id, total);
|
|
2
|
+
create index orders_paid_idx on sales.orders (organization_id, paid);
|
|
3
|
+
create index invoices_issued_at_idx on sales.invoices (organization_id, issued_at);
|
|
4
|
+
create index stock_moves_quantity_idx on inventory.stock_moves (organization_id, quantity);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
alter table sales.invoices
|
|
2
|
+
alter column subtotal set default 0,
|
|
3
|
+
alter column vat set default 0,
|
|
4
|
+
alter column total set default 0;
|
|
5
|
+
alter table sales.orders alter column total set default 0;
|
|
6
|
+
|
|
7
|
+
-- Invoice totals: subtotal from the lines, VAT from the invoice rate.
|
|
8
|
+
create function sales.refresh_invoice_totals(ids bigint[]) returns void language sql as $$
|
|
9
|
+
update sales.invoices i
|
|
10
|
+
set subtotal = s.subtotal,
|
|
11
|
+
vat = round(s.subtotal * i.vat_rate / 100, 2),
|
|
12
|
+
total = s.subtotal + round(s.subtotal * i.vat_rate / 100, 2)
|
|
13
|
+
from (
|
|
14
|
+
select inv.id, coalesce((select sum(l.line_total) from sales.invoice_lines l where l.invoice_id = inv.id), 0) as subtotal
|
|
15
|
+
from sales.invoices inv
|
|
16
|
+
where inv.id = any (ids)
|
|
17
|
+
) s
|
|
18
|
+
where i.id = s.id
|
|
19
|
+
and (i.subtotal, i.vat, i.total) is distinct from (s.subtotal, round(s.subtotal * i.vat_rate / 100, 2), s.subtotal + round(s.subtotal * i.vat_rate / 100, 2))
|
|
20
|
+
$$;
|
|
21
|
+
|
|
22
|
+
create function sales.invoice_lines_inserted() returns trigger language plpgsql as $$
|
|
23
|
+
begin
|
|
24
|
+
perform sales.refresh_invoice_totals(array(select distinct invoice_id from new_rows));
|
|
25
|
+
return null;
|
|
26
|
+
end $$;
|
|
27
|
+
|
|
28
|
+
create function sales.invoice_lines_updated() returns trigger language plpgsql as $$
|
|
29
|
+
begin
|
|
30
|
+
perform sales.refresh_invoice_totals(array(select invoice_id from old_rows union select invoice_id from new_rows));
|
|
31
|
+
return null;
|
|
32
|
+
end $$;
|
|
33
|
+
|
|
34
|
+
create function sales.invoice_lines_deleted() returns trigger language plpgsql as $$
|
|
35
|
+
begin
|
|
36
|
+
perform sales.refresh_invoice_totals(array(select distinct invoice_id from old_rows));
|
|
37
|
+
return null;
|
|
38
|
+
end $$;
|
|
39
|
+
|
|
40
|
+
-- Transition tables cannot be combined with several events, so one trigger per event.
|
|
41
|
+
create trigger invoice_lines_inserted after insert on sales.invoice_lines
|
|
42
|
+
referencing new table as new_rows for each statement execute function sales.invoice_lines_inserted();
|
|
43
|
+
create trigger invoice_lines_updated after update on sales.invoice_lines
|
|
44
|
+
referencing old table as old_rows new table as new_rows for each statement execute function sales.invoice_lines_updated();
|
|
45
|
+
create trigger invoice_lines_deleted after delete on sales.invoice_lines
|
|
46
|
+
referencing old table as old_rows for each statement execute function sales.invoice_lines_deleted();
|
|
47
|
+
|
|
48
|
+
-- A new or re-rated invoice recomputes VAT and total from its current subtotal.
|
|
49
|
+
create function sales.invoice_apply_vat() returns trigger language plpgsql as $$
|
|
50
|
+
begin
|
|
51
|
+
new.vat := round(new.subtotal * new.vat_rate / 100, 2);
|
|
52
|
+
new.total := new.subtotal + new.vat;
|
|
53
|
+
return new;
|
|
54
|
+
end $$;
|
|
55
|
+
|
|
56
|
+
create trigger invoice_apply_vat before insert or update of vat_rate on sales.invoices
|
|
57
|
+
for each row execute function sales.invoice_apply_vat();
|
|
58
|
+
|
|
59
|
+
-- Order totals: sum of the lines.
|
|
60
|
+
create function sales.refresh_order_totals(ids uuid[]) returns void language sql as $$
|
|
61
|
+
update sales.orders o
|
|
62
|
+
set total = s.total
|
|
63
|
+
from (
|
|
64
|
+
select ord.id, coalesce((select sum(l.quantity * l.unit_price) from sales.order_lines l where l.order_id = ord.id), 0) as total
|
|
65
|
+
from sales.orders ord
|
|
66
|
+
where ord.id = any (ids)
|
|
67
|
+
) s
|
|
68
|
+
where o.id = s.id and o.total is distinct from s.total
|
|
69
|
+
$$;
|
|
70
|
+
|
|
71
|
+
create function sales.order_lines_inserted() returns trigger language plpgsql as $$
|
|
72
|
+
begin
|
|
73
|
+
perform sales.refresh_order_totals(array(select distinct order_id from new_rows));
|
|
74
|
+
return null;
|
|
75
|
+
end $$;
|
|
76
|
+
|
|
77
|
+
create function sales.order_lines_updated() returns trigger language plpgsql as $$
|
|
78
|
+
begin
|
|
79
|
+
perform sales.refresh_order_totals(array(select order_id from old_rows union select order_id from new_rows));
|
|
80
|
+
return null;
|
|
81
|
+
end $$;
|
|
82
|
+
|
|
83
|
+
create function sales.order_lines_deleted() returns trigger language plpgsql as $$
|
|
84
|
+
begin
|
|
85
|
+
perform sales.refresh_order_totals(array(select distinct order_id from old_rows));
|
|
86
|
+
return null;
|
|
87
|
+
end $$;
|
|
88
|
+
|
|
89
|
+
create trigger order_lines_inserted after insert on sales.order_lines
|
|
90
|
+
referencing new table as new_rows for each statement execute function sales.order_lines_inserted();
|
|
91
|
+
create trigger order_lines_updated after update on sales.order_lines
|
|
92
|
+
referencing old table as old_rows new table as new_rows for each statement execute function sales.order_lines_updated();
|
|
93
|
+
create trigger order_lines_deleted after delete on sales.order_lines
|
|
94
|
+
referencing old table as old_rows for each statement execute function sales.order_lines_deleted();
|
|
95
|
+
|
|
96
|
+
-- Recalculate existing rows once.
|
|
97
|
+
select sales.refresh_invoice_totals(array(select id from sales.invoices));
|
|
98
|
+
select sales.refresh_order_totals(array(select id from sales.orders));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- The auth schema changes this version of Protobase needs, written by `protobase auth migration`.
|
|
2
|
+
-- Applied with the project's other migrations; edit or remove statements the project handles differently.
|
|
3
|
+
|
|
4
|
+
create schema if not exists "auth";
|
|
5
|
+
|
|
6
|
+
alter table "auth"."user" add column if not exists "last_organization_id" text;
|
|
7
|
+
|
|
8
|
+
alter table "auth"."session" add column if not exists "active_organization_id" text;
|
|
9
|
+
|
|
10
|
+
create table if not exists "auth"."organization" ("id" text not null primary key, "name" text not null, "slug" text not null unique, "logo" text, "created_at" timestamptz not null, "metadata" text);
|
|
11
|
+
|
|
12
|
+
create table if not exists "auth"."member" ("id" text not null primary key, "organization_id" text not null references "auth"."organization" ("id") on delete cascade, "user_id" text not null references "auth"."user" ("id") on delete cascade, "role" text not null, "created_at" timestamptz not null, "app_roles" jsonb);
|
|
13
|
+
|
|
14
|
+
create table if not exists "auth"."invitation" ("id" text not null primary key, "organization_id" text not null references "auth"."organization" ("id") on delete cascade, "email" text not null, "role" text, "status" text not null, "expires_at" timestamptz not null, "created_at" timestamptz default CURRENT_TIMESTAMP not null, "inviter_id" text not null references "auth"."user" ("id") on delete cascade, "app_roles" jsonb);
|
|
15
|
+
|
|
16
|
+
create index if not exists "member_organization_id_idx" on "auth"."member" ("organization_id");
|
|
17
|
+
|
|
18
|
+
create index if not exists "member_user_id_idx" on "auth"."member" ("user_id");
|
|
19
|
+
|
|
20
|
+
create index if not exists "invitation_organization_id_idx" on "auth"."invitation" ("organization_id");
|
|
21
|
+
|
|
22
|
+
create index if not exists "invitation_email_idx" on "auth"."invitation" ("email");
|