shutro-erp 0.3.0 → 0.3.1

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.
Files changed (96) hide show
  1. package/README.md +58 -58
  2. package/dist/account_portal/.kosh/bindings.js +1 -1
  3. package/dist/account_portal/.kosh/schema.d.ts +31 -1
  4. package/dist/accounting/.kosh/bindings.js +1 -1
  5. package/dist/accounting/.kosh/schema.d.ts +31 -1
  6. package/dist/assistant/routes.js +1 -1
  7. package/dist/automation/engine.js +1 -1
  8. package/dist/automation/web/messages.js +2 -2
  9. package/dist/cli/apps.d.ts +1 -1
  10. package/dist/cli/apps.js +14 -14
  11. package/dist/cli/bin.js +3 -0
  12. package/dist/cli/index.js +11 -11
  13. package/dist/cli/init.d.ts +1 -1
  14. package/dist/cli/init.js +58 -36
  15. package/dist/cli/launch.js +2 -2
  16. package/dist/cli/project.d.ts +7 -7
  17. package/dist/cli/project.js +25 -25
  18. package/dist/cli/prompt.js +2 -2
  19. package/dist/cli/templates.d.ts +3 -3
  20. package/dist/cli/templates.js +29 -27
  21. package/dist/cli/ui.d.ts +1 -8
  22. package/dist/cli/ui.js +17 -11
  23. package/dist/contacts/.kosh/bindings.js +1 -1
  24. package/dist/contacts/.kosh/schema.d.ts +31 -1
  25. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  26. package/dist/l10n_bd/.kosh/schema.d.ts +31 -1
  27. package/dist/l10n_in/.kosh/bindings.js +1 -1
  28. package/dist/l10n_in/.kosh/schema.d.ts +31 -1
  29. package/dist/l10n_in/einvoice.js +1 -1
  30. package/dist/l10n_in/web/messages.js +2 -2
  31. package/dist/mail/index.js +2 -2
  32. package/dist/modules-sdk/loader.js +2 -2
  33. package/dist/notify/messages.js +3 -3
  34. package/dist/notify/routes.js +1 -1
  35. package/dist/notify/web/messages.js +1 -1
  36. package/dist/portal/.kosh/bindings.js +1 -1
  37. package/dist/portal/.kosh/schema.d.ts +31 -1
  38. package/dist/product/.kosh/bindings.js +1 -1
  39. package/dist/product/.kosh/schema.d.ts +31 -1
  40. package/dist/project/.kosh/bindings.js +1 -1
  41. package/dist/project/.kosh/schema.d.ts +31 -1
  42. package/dist/project_portal/.kosh/bindings.js +1 -1
  43. package/dist/project_portal/.kosh/schema.d.ts +31 -1
  44. package/dist/purchase/.kosh/bindings.js +1 -1
  45. package/dist/purchase/.kosh/schema.d.ts +31 -1
  46. package/dist/purchase_account/.kosh/bindings.js +1 -1
  47. package/dist/purchase_account/.kosh/schema.d.ts +31 -1
  48. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  49. package/dist/purchase_portal/.kosh/schema.d.ts +31 -1
  50. package/dist/sale_account/.kosh/bindings.js +1 -1
  51. package/dist/sale_account/.kosh/schema.d.ts +31 -1
  52. package/dist/sale_portal/.kosh/bindings.js +1 -1
  53. package/dist/sale_portal/.kosh/schema.d.ts +31 -1
  54. package/dist/sale_project/.kosh/bindings.js +1 -1
  55. package/dist/sale_project/.kosh/schema.d.ts +31 -1
  56. package/dist/sales/.kosh/bindings.js +1 -1
  57. package/dist/sales/.kosh/schema.d.ts +31 -1
  58. package/dist/server/api/client.js +1 -1
  59. package/dist/server/api/openapi.js +17 -1
  60. package/dist/server/apps.js +5 -5
  61. package/dist/server/auth/api-keys.d.ts +13 -0
  62. package/dist/server/auth/api-keys.js +65 -0
  63. package/dist/server/auth/base.js +24 -0
  64. package/dist/server/auth/crypto.js +1 -1
  65. package/dist/server/auth/scopes.d.ts +12 -0
  66. package/dist/server/auth/scopes.js +51 -0
  67. package/dist/server/auth/service.d.ts +57 -1
  68. package/dist/server/auth/service.js +229 -5
  69. package/dist/server/demo.d.ts +1 -1
  70. package/dist/server/demo.js +1 -1
  71. package/dist/server/env-names.d.ts +2 -0
  72. package/dist/server/env-names.js +16 -0
  73. package/dist/server/host.js +5 -1
  74. package/dist/server/identity.d.ts +7 -0
  75. package/dist/server/index.d.ts +1 -0
  76. package/dist/server/index.js +1 -0
  77. package/dist/server/registry.js +1 -1
  78. package/dist/server/server.d.ts +5 -0
  79. package/dist/server/server.js +45 -5
  80. package/dist/settings/routes.js +1 -1
  81. package/dist/settings/web/ApiKeysPage.d.ts +8 -0
  82. package/dist/settings/web/ApiKeysPage.js +70 -0
  83. package/dist/settings/web/index.d.ts +1 -0
  84. package/dist/settings/web/index.js +4 -0
  85. package/dist/settings/web/messages.js +62 -0
  86. package/dist/storage/routes.js +1 -1
  87. package/dist/studio/build.js +1 -1
  88. package/dist/studio/generate.js +4 -4
  89. package/dist/studio/module.js +5 -5
  90. package/dist/studio/web/messages.js +2 -2
  91. package/dist/ui/app.js +26 -3
  92. package/dist/ui/i18n/messages.js +2 -2
  93. package/dist/ui/pwa/worker.js +2 -2
  94. package/dist/ui/registry.d.ts +15 -0
  95. package/dist/ui/registry.js +20 -0
  96. package/package.json +3 -3
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The starter project `kosh init` writes: an API server and the back-office app, both importing `shutro-erp`.
2
+ * The starter project `shutro-erp init` writes: an API server and the back-office app, both importing `shutro-erp`.
3
3
  * Server files run on Node.js directly (type stripping); the back-office is a Vite app proxying `/api` to the server.
4
4
  */
5
- /** The apps `kosh init` can install (base and Settings are always there), in the order the installer lists them. */
5
+ /** The apps `shutro-erp init` can install (base and Settings are always there), in the order the installer lists them. */
6
6
  export const APPS = {
7
7
  permissions: {
8
8
  label: 'Roles, field security and approvals',
@@ -105,8 +105,8 @@ export function packageJson(options, existing) {
105
105
  start: 'node --env-file=.env src/server.ts',
106
106
  web: 'vite',
107
107
  'build:web': 'vite build',
108
- migrate: 'kosh migrate',
109
- seed: 'kosh seed',
108
+ migrate: 'shutro-erp migrate',
109
+ seed: 'shutro-erp seed',
110
110
  'docker:up': 'docker compose up -d --build',
111
111
  }),
112
112
  engines: { node: '>=24', ...(base['engines'] ?? {}) },
@@ -130,7 +130,7 @@ export function modulesTs(_options) {
130
130
  const imports = apps.map((a) => `import { ${APPS[a].server} } from 'shutro-erp/${a}';`);
131
131
  return `/**
132
132
  * The apps this installation can install (the catalogue). The database records which are installed: Settings → Apps
133
- * in the back-office, or \`npx kosh apps\`, installs, disables and enables them. \`base\` (users, companies, sign-in)
133
+ * in the back-office, or \`npx shutro-erp apps\`, installs, disables and enables them. \`base\` (users, companies, sign-in)
134
134
  * and Settings are always installed. Add your own modules here (\`defineModule\` from \`shutro-erp/modules-sdk\`),
135
135
  * then install them from Settings → Apps.
136
136
  */
@@ -150,7 +150,7 @@ export const modules = [base, settings, realtime, notify, mail, automation, stud
150
150
  }
151
151
  export function serverTs() {
152
152
  return `/**
153
- * The Kosh API server: \`npm start\` (reads .env). It serves the installed apps of src/modules.ts, and switches to
153
+ * The Shutro ERP API server: \`npm start\` (reads .env). It serves the installed apps of src/modules.ts, and switches to
154
154
  * the new set without a restart when apps are installed or disabled (here or on another server).
155
155
  */
156
156
  import { createAppHost, startAppHost } from 'shutro-erp';
@@ -162,11 +162,11 @@ const host = await createAppHost({
162
162
  catalogue: modules,
163
163
  migrationsDir: 'migrations',
164
164
  ...(env['VALKEY_URL'] ? { valkeyUrl: env['VALKEY_URL'] } : {}),
165
- // Secure cookies need HTTPS: on by default in production (KOSH_SECURE_COOKIES=false for plain HTTP).
166
- auth: { secureCookies: (env['KOSH_SECURE_COOKIES'] ?? String(env['NODE_ENV'] === 'production')) === 'true' },
165
+ // Secure cookies need HTTPS: on by default in production (SHUTRO_ERP_SECURE_COOKIES=false for plain HTTP).
166
+ auth: { secureCookies: (env['SHUTRO_ERP_SECURE_COOKIES'] ?? env['KOSH_SECURE_COOKIES'] ?? String(env['NODE_ENV'] === 'production')) === 'true' },
167
167
  });
168
168
  const address = await startAppHost(host, { port: Number(env['PORT'] ?? 3000), host: env['HOST'] ?? '127.0.0.1' });
169
- process.stdout.write(\`Kosh API on \${address}\\n\`);
169
+ process.stdout.write(\`Shutro ERP API on \${address}\\n\`);
170
170
  `;
171
171
  }
172
172
  export function viteConfig(options) {
@@ -176,7 +176,7 @@ import react from '@vitejs/plugin-react';
176
176
  import { koshPwa } from 'shutro-erp/ui/vite';
177
177
  import { defineConfig } from 'vite';
178
178
 
179
- const api = process.env['KOSH_API'] ?? 'http://localhost:${options.port}';
179
+ const api = process.env['SHUTRO_ERP_API'] ?? process.env['KOSH_API'] ?? 'http://localhost:${options.port}';
180
180
 
181
181
  export default defineConfig({
182
182
  // koshPwa: the service worker (the app opens offline and gets web push)
@@ -263,7 +263,7 @@ export function manifest(options) {
263
263
  }
264
264
  /** The app icon (replace it with the company's). */
265
265
  export function icon() {
266
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="Kosh">
266
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="Shutro ERP">
267
267
  <rect width="512" height="512" rx="112" fill="#1d4ed8"/>
268
268
  <path d="M156 128h56v112l108-112h72L270 258l128 126h-74L220 282v102h-64z" fill="#fff"/>
269
269
  </svg>
@@ -273,7 +273,7 @@ export function indexCss() {
273
273
  return `@import "tailwindcss";
274
274
  @import "shutro-erp/ui/styles.css";
275
275
 
276
- /* Tailwind scans Kosh's components for the classes they use. */
276
+ /* Tailwind scans Shutro ERP's components for the classes they use. */
277
277
  @source "../../node_modules/shutro-erp/dist";
278
278
  `;
279
279
  }
@@ -286,7 +286,7 @@ var/
286
286
  }
287
287
  /** Three images from one build: the API (migrates, then serves) and nginx with the back-office. */
288
288
  export function dockerfile() {
289
- return `# Kosh images (kosh init). Built by compose.yaml: docker compose up -d --build
289
+ return `# Shutro ERP images (shutro-erp init). Built by compose.yaml: docker compose up -d --build
290
290
  FROM node:24-alpine AS build
291
291
  WORKDIR /app
292
292
  COPY package.json package-lock.json* ./
@@ -306,7 +306,7 @@ COPY --from=build /app/migrations ./migrations
306
306
  RUN mkdir -p /app/var/attachments && chown -R node:node /app/var
307
307
  USER node
308
308
  EXPOSE 3000
309
- CMD ["sh", "-c", "npx kosh migrate && node src/server.ts"]
309
+ CMD ["sh", "-c", "npx shutro-erp migrate && node src/server.ts"]
310
310
 
311
311
  # The back-office: static files, /api proxied to the API.
312
312
  FROM nginx:1.27-alpine AS web
@@ -315,7 +315,7 @@ COPY nginx.conf /etc/nginx/conf.d/default.conf
315
315
  `;
316
316
  }
317
317
  export function nginxConf() {
318
- return `# The back-office (kosh init): the built app, /api proxied to the API service.
318
+ return `# The back-office (shutro-erp init): the built app, /api proxied to the API service.
319
319
  server {
320
320
  listen 80;
321
321
  root /usr/share/nginx/html;
@@ -346,9 +346,9 @@ export function composeYaml(options) {
346
346
  const db = options.dockerDb ?? options.name.replace(/-/g, '_');
347
347
  /** A compose variable: `${NAME}` or `${NAME:-default}` (plain strings: nothing to escape). */
348
348
  const v = (name, fallback) => ['$', '{', name, fallback === undefined ? '' : fallback, '}'].join('');
349
- const user = v('POSTGRES_USER', ':-kosh');
349
+ const user = v('POSTGRES_USER', ':-shutro');
350
350
  const lines = [
351
- '# Kosh in Docker (kosh init).',
351
+ '# Shutro ERP in Docker (shutro-erp init).',
352
352
  '# docker compose up -d db only PostgreSQL (the API runs with npm start)',
353
353
  '# docker compose up -d --build everything: the back-office on http://localhost:8080',
354
354
  `name: ${options.name}`,
@@ -367,7 +367,7 @@ export function composeYaml(options) {
367
367
  ' - db:/var/lib/postgresql',
368
368
  ' healthcheck:',
369
369
  // $$ is a literal $ for compose: the shell in the container expands the variable.
370
- ` test: ["CMD-SHELL", "pg_isready -U $${v('POSTGRES_USER', ':-kosh')}"]`,
370
+ ` test: ["CMD-SHELL", "pg_isready -U $${v('POSTGRES_USER', ':-shutro')}"]`,
371
371
  ' interval: 5s',
372
372
  ' retries: 30',
373
373
  '',
@@ -379,9 +379,11 @@ export function composeYaml(options) {
379
379
  ' env_file: .env',
380
380
  ' environment:',
381
381
  ` DATABASE_URL: ${v('DOCKER_DATABASE_URL', `:-postgres://${user}:${v('POSTGRES_PASSWORD')}@db:5432/${v('POSTGRES_DB', `:-${db}`)}`)}`,
382
- ...(options.fileStorage === false ? [] : [' KOSH_STORAGE_DIR: /app/var/attachments']),
382
+ ...(options.fileStorage === false
383
+ ? []
384
+ : [' SHUTRO_ERP_STORAGE_DIR: /app/var/attachments']),
383
385
  ' # Plain HTTP on localhost; set "true" when the site is served over HTTPS.',
384
- ' KOSH_SECURE_COOKIES: "false"',
386
+ ' SHUTRO_ERP_SECURE_COOKIES: "false"',
385
387
  ' depends_on:',
386
388
  ' db:',
387
389
  ' condition: service_healthy',
@@ -431,19 +433,19 @@ export function tsconfig() {
431
433
  export function readme(options) {
432
434
  return `# ${options.name}
433
435
 
434
- A [Kosh](https://www.npmjs.com/package/shutro-erp) installation, created with \`kosh init\`.
436
+ A [Shutro ERP](https://www.npmjs.com/package/shutro-erp) installation, created with \`shutro-erp init\`.
435
437
 
436
438
  \`\`\`sh
437
439
  npm install
438
440
  npm start # the API on http://localhost:${options.port}
439
441
  npm run web # the back-office on http://localhost:${options.webPort ?? 5173} (sign in as the administrator)
440
- npx kosh migrate # after changing modules: plan and apply database migrations, load seed data
441
- npx kosh seed # load seed data again
442
+ npx shutro-erp migrate # after changing modules: plan and apply database migrations, load seed data
443
+ npx shutro-erp seed # load seed data again
442
444
  # Settings → Apps in the back-office installs, enables and disables apps (or, in the terminal:)
443
- npx kosh apps # the apps: installed, disabled, available
444
- npx kosh apps install <name> # install an app (and what it needs) in one transaction
445
- npx kosh apps disable <name> # switch an app off; its data stays (npx kosh apps enable <name>)
446
- npx kosh module create <name> # a new module in src/modules/<name>/
445
+ npx shutro-erp apps # the apps: installed, disabled, available
446
+ npx shutro-erp apps install <name> # install an app (and what it needs) in one transaction
447
+ npx shutro-erp apps disable <name> # switch an app off; its data stays (npx shutro-erp apps enable <name>)
448
+ npx shutro-erp module create <name> # a new module in src/modules/<name>/
447
449
  docker compose up -d --build # everything in Docker: http://localhost:8080
448
450
  \`\`\`
449
451
 
package/dist/cli/ui.d.ts CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * The installer's look in the terminal, without dependencies: a logo, numbered steps, spinners for long work,
3
- * status lines (✔ ▲ ✖) and a summary card. On a Unicode terminal everything hangs on one rail down the left (│),
4
- * the questions of the interactive prompter included (`tui.ts`). Colour, Unicode and animation are detected from
5
- * the terminal (NO_COLOR, FORCE_COLOR, KOSH_ASCII=1, TERM=dumb, Windows consoles without Unicode); output that is not
6
- * a terminal (tests, logs, CI) gets plain lines with the same words and no escape codes.
7
- */
8
1
  /** What the terminal can show. */
9
2
  export interface Terminal {
10
3
  readonly color: boolean;
@@ -23,7 +16,7 @@ type Env = Readonly<Record<string, string | undefined>>;
23
16
  export declare function detectTerminal(stream: {
24
17
  readonly isTTY?: boolean;
25
18
  readonly columns?: number;
26
- }, env?: Env, platform?: string): Terminal;
19
+ }, env?: Env, platform?: string, release?: string): Terminal;
27
20
  export declare const ESC = "\u001B[";
28
21
  /** The colours and weights the installer uses. */
29
22
  export type Style = 'bold' | 'dim' | 'inverse' | 'underline' | 'green' | 'yellow' | 'red' | 'cyan' | 'magenta' | 'gray';
package/dist/cli/ui.js CHANGED
@@ -2,9 +2,10 @@
2
2
  * The installer's look in the terminal, without dependencies: a logo, numbered steps, spinners for long work,
3
3
  * status lines (✔ ▲ ✖) and a summary card. On a Unicode terminal everything hangs on one rail down the left (│),
4
4
  * the questions of the interactive prompter included (`tui.ts`). Colour, Unicode and animation are detected from
5
- * the terminal (NO_COLOR, FORCE_COLOR, KOSH_ASCII=1, TERM=dumb, Windows consoles without Unicode); output that is not
5
+ * the terminal (NO_COLOR, FORCE_COLOR, SHUTRO_ERP_ASCII=1, TERM=dumb, Windows consoles without Unicode); output that is not
6
6
  * a terminal (tests, logs, CI) gets plain lines with the same words and no escape codes.
7
7
  */
8
+ import { release as osRelease } from 'node:os';
8
9
  /** Plain output: logs, tests, pipes. */
9
10
  export const PLAIN_TERMINAL = Object.freeze({
10
11
  color: false,
@@ -14,7 +15,7 @@ export const PLAIN_TERMINAL = Object.freeze({
14
15
  columns: 80,
15
16
  });
16
17
  /** Detects what `stream` (process.stdout) can show. */
17
- export function detectTerminal(stream, env = process.env, platform = process.platform) {
18
+ export function detectTerminal(stream, env = process.env, platform = process.platform, release = osRelease()) {
18
19
  const tty = stream.isTTY === true && env['TERM'] !== 'dumb' && env['CI'] === undefined;
19
20
  const forced = env['FORCE_COLOR'] !== undefined && env['FORCE_COLOR'] !== '0';
20
21
  const color = env['NO_COLOR'] === undefined && (forced || tty);
@@ -24,11 +25,16 @@ export function detectTerminal(stream, env = process.env, platform = process.pla
24
25
  env['ConEmuTask'] === '{cmd::Cmder}' ||
25
26
  env['TERM'] === 'xterm-256color' ||
26
27
  env['TERM'] === 'alacritty';
27
- const unicode = env['KOSH_ASCII'] !== '1' && (platform === 'win32' ? modernWindows : env['TERM'] !== 'linux');
28
+ // Windows 10 and later: the console host shows box drawing and colours too (UTF-8, virtual terminal); pictographs
29
+ // only in the modern terminals. Older Windows keeps ASCII.
30
+ const windows10 = Number(release.split('.')[0]) >= 10;
31
+ const unicode = env['KOSH_ASCII'] !== '1' &&
32
+ env['SHUTRO_ERP_ASCII'] !== '1' &&
33
+ (platform === 'win32' ? modernWindows || windows10 : env['TERM'] !== 'linux');
28
34
  return {
29
35
  color,
30
36
  unicode,
31
- emoji: unicode && tty,
37
+ emoji: unicode && tty && (platform !== 'win32' || modernWindows),
32
38
  animate: tty,
33
39
  columns: Math.max(40, Math.min(stream.columns ?? 80, 120)),
34
40
  };
@@ -92,12 +98,12 @@ export const ICONS = {
92
98
  rocket: ['🚀', '◆'],
93
99
  };
94
100
  const LOGO = [
95
- '██╗ ██╗ ██████╗ ███████╗██╗ ██╗',
96
- '██║ ██╔╝██╔═══██╗██╔════╝██║ ██║',
97
- '█████╔╝ ██║ ██║███████╗███████║',
98
- '██╔═██╗ ██║ ██║╚════██║██╔══██║',
99
- '██║ ██╗╚██████╔╝███████║██║ ██║',
100
- '╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝',
101
+ '███████╗██╗ ██╗██╗ ██╗████████╗██████╗ ██████╗',
102
+ '██╔════╝██║ ██║██║ ██║╚══██╔══╝██╔══██╗██╔═══██╗',
103
+ '███████╗███████║██║ ██║ ██║ ██████╔╝██║ ██║',
104
+ '╚════██║██╔══██║██║ ██║ ██║ ██╔══██╗██║ ██║',
105
+ '███████║██║ ██║╚██████╔╝ ██║ ██║ ██║╚██████╔╝',
106
+ '╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝',
101
107
  ];
102
108
  /** Cyan to violet, one 256-colour step per logo line. */
103
109
  const GRADIENT = [51, 45, 39, 33, 63, 99];
@@ -141,7 +147,7 @@ export function createUi(out, terminal = PLAIN_TERMINAL, options = {}) {
141
147
  icon,
142
148
  async banner(subtitle) {
143
149
  if (!terminal.unicode) {
144
- write(`\n ${s('bold', 'K O S H')} ${subtitle}\n`);
150
+ write(`\n ${s('bold', 'S H U T R O E R P')} ${subtitle}\n`);
145
151
  return;
146
152
  }
147
153
  write('\n');
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 07f36b4355890ebe
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2271aaa68e1a18a5
2
2
  // Bindings of module `contacts`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';
@@ -1,10 +1,11 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 07f36b4355890ebe
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2271aaa68e1a18a5
2
2
  // Schema view of module `contacts` (RFC 0002 §7.7): the models it owns or depends on, with the extensions it can see.
3
3
  import type { F } from '../../server/orm.js';
4
4
 
5
5
  /** Model name → model type (RFC 0002 §7.7). */
6
6
  export interface SchemaView {
7
7
  readonly 'audit.event': AuditEvent;
8
+ readonly 'auth.api_key': AuthApiKey;
8
9
  readonly 'auth.credential': AuthCredential;
9
10
  readonly 'auth.session': AuthSession;
10
11
  readonly 'auth.user_grant': AuthUserGrant;
@@ -42,6 +43,34 @@ export interface AuditEvent {
42
43
  export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
43
44
  }
44
45
 
46
+ /** `auth.api_key` (owner: base). */
47
+ export interface AuthApiKey {
48
+ readonly name: 'auth.api_key';
49
+ readonly fields: {
50
+ readonly id: F.Id<'auth.api_key'>;
51
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
52
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
53
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
54
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
55
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
56
+ readonly name: F.Char<{ readonly required: true }>;
57
+ readonly user: F.Many2one<'res.users', { readonly required: true }>;
58
+ readonly tokenHash: F.Char<{ readonly required: true }>;
59
+ readonly prefix: F.Char<{ readonly required: true }>;
60
+ readonly scopes: F.Text<{ readonly required: true }>;
61
+ readonly expiresAt: F.Datetime<{ readonly required: true }>;
62
+ readonly lastUsedAt: F.Datetime;
63
+ readonly lastUsedIp: F.Char;
64
+ readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
65
+ readonly issuedBy: F.Many2one<'res.users'>;
66
+ };
67
+ readonly methods: AuthApiKeyMethods;
68
+ }
69
+
70
+ /** Methods of `auth.api_key`: declared ones and transitions. */
71
+ export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
72
+ }
73
+
45
74
  /** `auth.credential` (owner: base). */
46
75
  export interface AuthCredential {
47
76
  readonly name: 'auth.credential';
@@ -260,6 +289,7 @@ export interface ResUsers {
260
289
  readonly companies: F.Many2many<'res.company', { readonly required: true }>;
261
290
  readonly lang: F.Char<{ readonly default: true }>;
262
291
  readonly tz: F.Char<{ readonly default: true }>;
292
+ readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
263
293
  };
264
294
  readonly methods: ResUsersMethods;
265
295
  }
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 3a5b3163d9a0a2af
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 52fb4c0464f10e60
2
2
  // Bindings of module `l10n_bd`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 3a5b3163d9a0a2af
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 52fb4c0464f10e60
2
2
  // Schema view of module `l10n_bd` (RFC 0002 §7.7): the models it owns or depends on, with the extensions it can see.
3
3
  import type { F } from '../../server/orm.js';
4
4
 
@@ -22,6 +22,7 @@ export interface SchemaView {
22
22
  readonly 'approval.policy': ApprovalPolicy;
23
23
  readonly 'approval.request': ApprovalRequest;
24
24
  readonly 'audit.event': AuditEvent;
25
+ readonly 'auth.api_key': AuthApiKey;
25
26
  readonly 'auth.credential': AuthCredential;
26
27
  readonly 'auth.session': AuthSession;
27
28
  readonly 'auth.user_grant': AuthUserGrant;
@@ -583,6 +584,34 @@ export interface AuditEvent {
583
584
  export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
584
585
  }
585
586
 
587
+ /** `auth.api_key` (owner: base). */
588
+ export interface AuthApiKey {
589
+ readonly name: 'auth.api_key';
590
+ readonly fields: {
591
+ readonly id: F.Id<'auth.api_key'>;
592
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
593
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
594
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
595
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
596
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
597
+ readonly name: F.Char<{ readonly required: true }>;
598
+ readonly user: F.Many2one<'res.users', { readonly required: true }>;
599
+ readonly tokenHash: F.Char<{ readonly required: true }>;
600
+ readonly prefix: F.Char<{ readonly required: true }>;
601
+ readonly scopes: F.Text<{ readonly required: true }>;
602
+ readonly expiresAt: F.Datetime<{ readonly required: true }>;
603
+ readonly lastUsedAt: F.Datetime;
604
+ readonly lastUsedIp: F.Char;
605
+ readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
606
+ readonly issuedBy: F.Many2one<'res.users'>;
607
+ };
608
+ readonly methods: AuthApiKeyMethods;
609
+ }
610
+
611
+ /** Methods of `auth.api_key`: declared ones and transitions. */
612
+ export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
613
+ }
614
+
586
615
  /** `auth.credential` (owner: base). */
587
616
  export interface AuthCredential {
588
617
  readonly name: 'auth.credential';
@@ -1224,6 +1253,7 @@ export interface ResUsers {
1224
1253
  readonly companies: F.Many2many<'res.company', { readonly required: true }>;
1225
1254
  readonly lang: F.Char<{ readonly default: true }>;
1226
1255
  readonly tz: F.Char<{ readonly default: true }>;
1256
+ readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
1227
1257
  };
1228
1258
  readonly methods: ResUsersMethods;
1229
1259
  }
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b77299ae8f7c3001
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 3a48057268d9c891
2
2
  // Bindings of module `l10n_in`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b77299ae8f7c3001
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 3a48057268d9c891
2
2
  // Schema view of module `l10n_in` (RFC 0002 §7.7): the models it owns or depends on, with the extensions it can see.
3
3
  import type { F } from '../../server/orm.js';
4
4
 
@@ -22,6 +22,7 @@ export interface SchemaView {
22
22
  readonly 'approval.policy': ApprovalPolicy;
23
23
  readonly 'approval.request': ApprovalRequest;
24
24
  readonly 'audit.event': AuditEvent;
25
+ readonly 'auth.api_key': AuthApiKey;
25
26
  readonly 'auth.credential': AuthCredential;
26
27
  readonly 'auth.session': AuthSession;
27
28
  readonly 'auth.user_grant': AuthUserGrant;
@@ -605,6 +606,34 @@ export interface AuditEvent {
605
606
  export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
606
607
  }
607
608
 
609
+ /** `auth.api_key` (owner: base). */
610
+ export interface AuthApiKey {
611
+ readonly name: 'auth.api_key';
612
+ readonly fields: {
613
+ readonly id: F.Id<'auth.api_key'>;
614
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
615
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
616
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
617
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
618
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
619
+ readonly name: F.Char<{ readonly required: true }>;
620
+ readonly user: F.Many2one<'res.users', { readonly required: true }>;
621
+ readonly tokenHash: F.Char<{ readonly required: true }>;
622
+ readonly prefix: F.Char<{ readonly required: true }>;
623
+ readonly scopes: F.Text<{ readonly required: true }>;
624
+ readonly expiresAt: F.Datetime<{ readonly required: true }>;
625
+ readonly lastUsedAt: F.Datetime;
626
+ readonly lastUsedIp: F.Char;
627
+ readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
628
+ readonly issuedBy: F.Many2one<'res.users'>;
629
+ };
630
+ readonly methods: AuthApiKeyMethods;
631
+ }
632
+
633
+ /** Methods of `auth.api_key`: declared ones and transitions. */
634
+ export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
635
+ }
636
+
608
637
  /** `auth.credential` (owner: base). */
609
638
  export interface AuthCredential {
610
639
  readonly name: 'auth.credential';
@@ -1286,6 +1315,7 @@ export interface ResUsers {
1286
1315
  readonly companies: F.Many2many<'res.company', { readonly required: true }>;
1287
1316
  readonly lang: F.Char<{ readonly default: true }>;
1288
1317
  readonly tz: F.Char<{ readonly default: true }>;
1318
+ readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
1289
1319
  };
1290
1320
  readonly methods: ResUsersMethods;
1291
1321
  }
@@ -280,7 +280,7 @@ export async function einvoiceState(env, id) {
280
280
  },
281
281
  };
282
282
  }
283
- const unavailable = () => new HttpProblem(409, 'no-gst-provider', 'E-invoicing is not set up', 'No IRP / e-way bill provider is configured for this installation (KOSH_GST_PROVIDER).');
283
+ const unavailable = () => new HttpProblem(409, 'no-gst-provider', 'E-invoicing is not set up', 'No IRP / e-way bill provider is configured for this installation (SHUTRO_ERP_GST_PROVIDER).');
284
284
  /**
285
285
  * Registers a document with the IRP. The document must be readable and the user may post (the right to e-invoice
286
286
  * goes with it). A refusal, here or by the IRP, is kept on the document and answered with 422.
@@ -43,7 +43,7 @@ export const messages = {
43
43
  save: 'Save',
44
44
  saved: 'Saved.',
45
45
  provider: 'IRP provider: {{provider}}',
46
- noProvider: 'No IRP provider is set up (KOSH_GST_PROVIDER): e-invoicing is unavailable.',
46
+ noProvider: 'No IRP provider is set up (SHUTRO_ERP_GST_PROVIDER): e-invoicing is unavailable.',
47
47
  },
48
48
  gst: {
49
49
  title: 'GST returns',
@@ -122,7 +122,7 @@ export const messages = {
122
122
  save: 'সংরক্ষণ',
123
123
  saved: 'সংরক্ষিত।',
124
124
  provider: 'IRP প্রদানকারী: {{provider}}',
125
- noProvider: 'কোনো IRP প্রদানকারী নেই (KOSH_GST_PROVIDER): ই-চালান অনুপলব্ধ।',
125
+ noProvider: 'কোনো IRP প্রদানকারী নেই (SHUTRO_ERP_GST_PROVIDER): ই-চালান অনুপলব্ধ।',
126
126
  },
127
127
  gst: {
128
128
  title: 'জিএসটি রিটার্ন',
@@ -40,14 +40,14 @@ export function mailerFromEnv(env = process.env) {
40
40
  const url = env['SMTP_URL'];
41
41
  if (url === undefined || url === '')
42
42
  return undefined;
43
- return createSmtpMailer({ url, from: env['MAIL_FROM'] ?? 'Kosh <kosh@localhost>' });
43
+ return createSmtpMailer({ url, from: env['MAIL_FROM'] ?? 'Shutro ERP <shutro-erp@localhost>' });
44
44
  }
45
45
  /** A mailer that keeps what it is given (tests, dry runs). */
46
46
  export function createMemoryMailer() {
47
47
  const sent = [];
48
48
  return {
49
49
  name: 'memory',
50
- from: 'Kosh <kosh@localhost>',
50
+ from: 'Shutro ERP <shutro-erp@localhost>',
51
51
  sent,
52
52
  async send(mail) {
53
53
  sent.push(mail);
@@ -63,11 +63,11 @@ export function loadModules(roots, options) {
63
63
  const actions = new Map();
64
64
  for (const m of modules) {
65
65
  if (!satisfies(options.koshVersion, m.kosh))
66
- fail(`Module '${m.name}' needs Kosh ${m.kosh}; this is Kosh ${options.koshVersion}.`, 'Upgrade the module or the framework.');
66
+ fail(`Module '${m.name}' needs Shutro ERP ${m.kosh}; this is Shutro ERP ${options.koshVersion}.`, 'Upgrade the module or the framework.');
67
67
  const deps = closure(m);
68
68
  for (const sd of m.shutro.depends)
69
69
  if (!deps.has(sd.name))
70
- fail(`Module '${m.name}' uses the Shutro module '${sd.name}' but no Kosh module it depends on provides it.`, `Add the Kosh module '${sd.name}' to depends.`);
70
+ fail(`Module '${m.name}' uses the Shutro module '${sd.name}' but no Shutro ERP module it depends on provides it.`, `Add the Shutro ERP module '${sd.name}' to depends.`);
71
71
  // Groups an access level may grant: this module's and its dependencies'.
72
72
  const groups = new Set(m.shutro.groups.map((g) => g.code));
73
73
  for (const d of deps)
@@ -13,9 +13,9 @@ export const TEXTS = {
13
13
  },
14
14
  states: { approved: 'approved', rejected: 'rejected' },
15
15
  email: {
16
- open: 'Open in Kosh',
17
- why: 'You receive this email because of your notification settings in Kosh.',
18
- digestSubject: 'Your Kosh summary: {{count}} notifications',
16
+ open: 'Open in Shutro ERP',
17
+ why: 'You receive this email because of your notification settings in Shutro ERP.',
18
+ digestSubject: 'Your Shutro ERP summary: {{count}} notifications',
19
19
  digestIntro: 'What happened since your last summary, by record.',
20
20
  },
21
21
  },
@@ -50,7 +50,7 @@ export function registerNotifyRoutes({ app }, options = {}) {
50
50
  app.get('/api/v1/notifications/push/key', async (request) => {
51
51
  await request.env(async () => undefined); // signed in
52
52
  if (options.pusher === undefined)
53
- throw new HttpProblem(404, 'push-not-configured', 'Push is not set up', 'This installation has no VAPID keys.', 'Set VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY and VAPID_SUBJECT (kosh init writes them).');
53
+ throw new HttpProblem(404, 'push-not-configured', 'Push is not set up', 'This installation has no VAPID keys.', 'Set VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY and VAPID_SUBJECT (shutro-erp init writes them).');
54
54
  return { publicKey: options.pusher.publicKey };
55
55
  });
56
56
  app.post('/api/v1/notifications/push', async (request) => {
@@ -11,7 +11,7 @@ export const messages = {
11
11
  push: {
12
12
  off: 'Push notifications on this device are off',
13
13
  on: 'Push notifications on this device are on',
14
- denied: 'This browser blocks notifications from Kosh',
14
+ denied: 'This browser blocks notifications from Shutro ERP',
15
15
  busy: 'One moment…',
16
16
  turnOn: 'Turn on',
17
17
  turnOff: 'Turn off',
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 435daa394f2704fc
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: bae647ca883975a1
2
2
  // Bindings of module `portal`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';
@@ -1,10 +1,11 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 435daa394f2704fc
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: bae647ca883975a1
2
2
  // Schema view of module `portal` (RFC 0002 §7.7): the models it owns or depends on, with the extensions it can see.
3
3
  import type { F } from '../../server/orm.js';
4
4
 
5
5
  /** Model name → model type (RFC 0002 §7.7). */
6
6
  export interface SchemaView {
7
7
  readonly 'audit.event': AuditEvent;
8
+ readonly 'auth.api_key': AuthApiKey;
8
9
  readonly 'auth.credential': AuthCredential;
9
10
  readonly 'auth.session': AuthSession;
10
11
  readonly 'auth.user_grant': AuthUserGrant;
@@ -45,6 +46,34 @@ export interface AuditEvent {
45
46
  export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
46
47
  }
47
48
 
49
+ /** `auth.api_key` (owner: base). */
50
+ export interface AuthApiKey {
51
+ readonly name: 'auth.api_key';
52
+ readonly fields: {
53
+ readonly id: F.Id<'auth.api_key'>;
54
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
55
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
56
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
57
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
58
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
59
+ readonly name: F.Char<{ readonly required: true }>;
60
+ readonly user: F.Many2one<'res.users', { readonly required: true }>;
61
+ readonly tokenHash: F.Char<{ readonly required: true }>;
62
+ readonly prefix: F.Char<{ readonly required: true }>;
63
+ readonly scopes: F.Text<{ readonly required: true }>;
64
+ readonly expiresAt: F.Datetime<{ readonly required: true }>;
65
+ readonly lastUsedAt: F.Datetime;
66
+ readonly lastUsedIp: F.Char;
67
+ readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
68
+ readonly issuedBy: F.Many2one<'res.users'>;
69
+ };
70
+ readonly methods: AuthApiKeyMethods;
71
+ }
72
+
73
+ /** Methods of `auth.api_key`: declared ones and transitions. */
74
+ export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
75
+ }
76
+
48
77
  /** `auth.credential` (owner: base). */
49
78
  export interface AuthCredential {
50
79
  readonly name: 'auth.credential';
@@ -336,6 +365,7 @@ export interface ResUsers {
336
365
  readonly companies: F.Many2many<'res.company', { readonly required: true }>;
337
366
  readonly lang: F.Char<{ readonly default: true }>;
338
367
  readonly tz: F.Char<{ readonly default: true }>;
368
+ readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
339
369
  readonly portalPartner: F.Many2one<'res.partner'>; // portal
340
370
  };
341
371
  readonly methods: ResUsersMethods;
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 5d099798463eb84a
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 86d7229d7b275957
2
2
  // Bindings of module `product`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';