shutro-erp 0.3.0 → 0.3.2
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/README.md +58 -58
- package/dist/account_portal/.kosh/bindings.js +1 -1
- package/dist/account_portal/.kosh/schema.d.ts +31 -1
- package/dist/account_portal/index.js +3 -2
- package/dist/accounting/.kosh/bindings.js +1 -1
- package/dist/accounting/.kosh/schema.d.ts +31 -1
- package/dist/accounting/index.js +4 -0
- package/dist/assistant/routes.js +1 -1
- package/dist/automation/engine.js +1 -1
- package/dist/automation/web/messages.js +2 -2
- package/dist/cli/apps.d.ts +1 -1
- package/dist/cli/apps.js +14 -14
- package/dist/cli/bin.js +3 -0
- package/dist/cli/index.js +11 -11
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +60 -36
- package/dist/cli/launch.js +10 -7
- package/dist/cli/project.d.ts +7 -7
- package/dist/cli/project.js +25 -25
- package/dist/cli/prompt.js +2 -2
- package/dist/cli/templates.d.ts +3 -3
- package/dist/cli/templates.js +33 -28
- package/dist/cli/ui.d.ts +1 -8
- package/dist/cli/ui.js +36 -12
- package/dist/contacts/.kosh/bindings.js +1 -1
- package/dist/contacts/.kosh/schema.d.ts +31 -1
- package/dist/integrations/inbound.d.ts +36 -0
- package/dist/integrations/inbound.js +171 -0
- package/dist/integrations/index.d.ts +18 -0
- package/dist/integrations/index.js +400 -0
- package/dist/integrations/models.d.ts +6 -0
- package/dist/integrations/models.js +119 -0
- package/dist/integrations/web/DeliveryTools.d.ts +4 -0
- package/dist/integrations/web/DeliveryTools.js +15 -0
- package/dist/integrations/web/EndpointTools.d.ts +7 -0
- package/dist/integrations/web/EndpointTools.js +25 -0
- package/dist/integrations/web/InboundTools.d.ts +4 -0
- package/dist/integrations/web/InboundTools.js +15 -0
- package/dist/integrations/web/WebhookTools.d.ts +7 -0
- package/dist/integrations/web/WebhookTools.js +25 -0
- package/dist/integrations/web/index.d.ts +16 -0
- package/dist/integrations/web/index.js +27 -0
- package/dist/integrations/web/messages.d.ts +2 -0
- package/dist/integrations/web/messages.js +53 -0
- package/dist/integrations/webhooks.d.ts +63 -0
- package/dist/integrations/webhooks.js +217 -0
- package/dist/l10n_bd/.kosh/bindings.js +1 -1
- package/dist/l10n_bd/.kosh/schema.d.ts +31 -1
- package/dist/l10n_in/.kosh/bindings.js +1 -1
- package/dist/l10n_in/.kosh/schema.d.ts +31 -1
- package/dist/l10n_in/einvoice.js +1 -1
- package/dist/l10n_in/web/messages.js +2 -2
- package/dist/mail/index.js +2 -2
- package/dist/modules-sdk/index.d.ts +1 -1
- package/dist/modules-sdk/loader.js +2 -2
- package/dist/modules-sdk/manifest.d.ts +48 -0
- package/dist/notify/messages.js +3 -3
- package/dist/notify/routes.js +1 -1
- package/dist/notify/web/messages.js +1 -1
- package/dist/payment/.kosh/bindings.d.ts +22 -0
- package/dist/payment/.kosh/bindings.js +15 -0
- package/dist/payment/.kosh/schema.d.ts +1553 -0
- package/dist/payment/index.d.ts +34 -0
- package/dist/payment/index.js +375 -0
- package/dist/payment/module.d.ts +6853 -0
- package/dist/payment/module.js +74 -0
- package/dist/payment/providers.d.ts +64 -0
- package/dist/payment/providers.js +77 -0
- package/dist/payment/settle.d.ts +11 -0
- package/dist/payment/settle.js +59 -0
- package/dist/payment/sslcommerz.d.ts +12 -0
- package/dist/payment/sslcommerz.js +185 -0
- package/dist/portal/.kosh/bindings.js +1 -1
- package/dist/portal/.kosh/schema.d.ts +31 -1
- package/dist/portal/routes-documents.js +5 -0
- package/dist/product/.kosh/bindings.js +1 -1
- package/dist/product/.kosh/schema.d.ts +31 -1
- package/dist/project/.kosh/bindings.js +1 -1
- package/dist/project/.kosh/schema.d.ts +31 -1
- package/dist/project_portal/.kosh/bindings.js +1 -1
- package/dist/project_portal/.kosh/schema.d.ts +31 -1
- package/dist/purchase/.kosh/bindings.js +1 -1
- package/dist/purchase/.kosh/schema.d.ts +31 -1
- package/dist/purchase_account/.kosh/bindings.js +1 -1
- package/dist/purchase_account/.kosh/schema.d.ts +31 -1
- package/dist/purchase_portal/.kosh/bindings.js +1 -1
- package/dist/purchase_portal/.kosh/schema.d.ts +31 -1
- package/dist/sale_account/.kosh/bindings.js +1 -1
- package/dist/sale_account/.kosh/schema.d.ts +31 -1
- package/dist/sale_portal/.kosh/bindings.js +1 -1
- package/dist/sale_portal/.kosh/schema.d.ts +31 -1
- package/dist/sale_project/.kosh/bindings.js +1 -1
- package/dist/sale_project/.kosh/schema.d.ts +31 -1
- package/dist/sales/.kosh/bindings.js +1 -1
- package/dist/sales/.kosh/schema.d.ts +31 -1
- package/dist/sales/index.js +2 -0
- package/dist/server/api/client.js +1 -1
- package/dist/server/api/openapi.js +17 -1
- package/dist/server/api/rest.d.ts +6 -1
- package/dist/server/api/rest.js +11 -0
- package/dist/server/apps.js +5 -5
- package/dist/server/auth/api-keys.d.ts +13 -0
- package/dist/server/auth/api-keys.js +65 -0
- package/dist/server/auth/base.js +24 -0
- package/dist/server/auth/crypto.js +1 -1
- package/dist/server/auth/scopes.d.ts +12 -0
- package/dist/server/auth/scopes.js +51 -0
- package/dist/server/auth/service.d.ts +57 -1
- package/dist/server/auth/service.js +229 -5
- package/dist/server/demo.d.ts +1 -1
- package/dist/server/demo.js +1 -1
- package/dist/server/env-names.d.ts +2 -0
- package/dist/server/env-names.js +16 -0
- package/dist/server/host.js +5 -1
- package/dist/server/identity.d.ts +7 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +2 -1
- package/dist/server/registry.js +1 -1
- package/dist/server/server.d.ts +5 -0
- package/dist/server/server.js +45 -5
- package/dist/settings/routes.js +1 -1
- package/dist/settings/web/ApiKeysPage.d.ts +8 -0
- package/dist/settings/web/ApiKeysPage.js +70 -0
- package/dist/settings/web/index.d.ts +1 -0
- package/dist/settings/web/index.js +4 -0
- package/dist/settings/web/messages.js +62 -0
- package/dist/storage/routes.js +1 -1
- package/dist/studio/build.js +1 -1
- package/dist/studio/generate.js +4 -4
- package/dist/studio/module.js +5 -5
- package/dist/studio/web/messages.js +2 -2
- package/dist/ui/app.js +26 -3
- package/dist/ui/i18n/messages.js +2 -2
- package/dist/ui/pwa/worker.js +2 -2
- package/dist/ui/registry.d.ts +15 -0
- package/dist/ui/registry.js +20 -0
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# shutro-erp
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Shutro ERP** is an ERP framework for Node.js, TypeScript and PostgreSQL, built on the
|
|
4
4
|
[Shutro](https://www.npmjs.com/package/shutro) ORM. Define a model once and get a REST API with OpenAPI, a typed
|
|
5
5
|
client, list / form / kanban / pivot / graph / calendar / gantt / map / dashboard views, exports, PDF documents and a
|
|
6
6
|
chatter on every record. Permissions are checked on the server on every route.
|
|
@@ -20,13 +20,13 @@ chatter on every record. Permissions are checked on the server on every route.
|
|
|
20
20
|
## Requirements
|
|
21
21
|
|
|
22
22
|
- **Node.js 24** or later (`node --version`)
|
|
23
|
-
- **PostgreSQL 16** or later, or **Docker** (
|
|
23
|
+
- **PostgreSQL 16** or later, or **Docker** (Shutro ERP can start PostgreSQL for you)
|
|
24
24
|
- An authenticator app for the administrator's second factor (Google Authenticator, Microsoft Authenticator,
|
|
25
25
|
1Password, …)
|
|
26
26
|
|
|
27
27
|
## Install with the terminal
|
|
28
28
|
|
|
29
|
-
One command creates the project, installs
|
|
29
|
+
One command creates the project, installs Shutro ERP and asks for everything it needs:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
32
|
npx shutro-erp@latest init my-erp # the project my-erp
|
|
@@ -37,7 +37,7 @@ npx shutro-erp@latest init # asks for the project name first
|
|
|
37
37
|
|
|
38
38
|
```sh
|
|
39
39
|
npm install shutro-erp # Shutro, the ORM, comes with it
|
|
40
|
-
npx
|
|
40
|
+
npx shutro-erp init
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
The setup asks for what the installation needs, one question at a time: type an answer or press **Enter** for
|
|
@@ -58,7 +58,7 @@ the default shown, pick from lists with **↑ ↓** and **Enter**, tick apps wit
|
|
|
58
58
|
| Authenticator code | `123456` | scan the printed secret, type a code; **keep the recovery codes** it prints |
|
|
59
59
|
|
|
60
60
|
Then it writes the project, creates the database tables (`migrations/0001_init.sql`), the company and the
|
|
61
|
-
administrator, and loads the apps' default data. **Then it starts
|
|
61
|
+
administrator, and loads the apps' default data. **Then it starts Shutro ERP for you**: it installs the project's
|
|
62
62
|
dependencies (with the package manager you ran it with: npm, pnpm, yarn or bun), starts the API and the
|
|
63
63
|
back-office, waits until both answer, and opens the back-office in your browser on the web port. Both keep
|
|
64
64
|
running in that terminal; **Ctrl+C** stops them. Sign in with the administrator's login, password and a code from
|
|
@@ -77,21 +77,21 @@ npm run web # the back-office on http://localhost:5173, in a second ter
|
|
|
77
77
|
```sh
|
|
78
78
|
npx shutro-erp@latest init my-erp --yes --docker-db --admin-password "a long passphrase here"
|
|
79
79
|
|
|
80
|
-
npx
|
|
80
|
+
npx shutro-erp init --yes \
|
|
81
81
|
--database-url postgres://postgres:postgres@localhost:5432/my_erp --create-database \
|
|
82
82
|
--company "Padma Traders" --currency BDT --lang bn --tz Asia/Dhaka \
|
|
83
83
|
--admin-login admin --admin-password "a long passphrase here" \
|
|
84
84
|
--apps permissions,reporting,messaging --storage-dir ./var/attachments --port 3000 --web-port 5173
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
With `--yes` nothing is asked, so the authenticator is not enrolled: run `npx
|
|
88
|
-
enrol it (administrator rights wait for it). `npx
|
|
87
|
+
With `--yes` nothing is asked, so the authenticator is not enrolled: run `npx shutro-erp init` again in a terminal to
|
|
88
|
+
enrol it (administrator rights wait for it). `npx shutro-erp init --help` lists every flag. Replies by email: set `MAIL_INBOUND_DOMAIN` in `.env` (the secrets are generated) and point your email provider's inbound webhook at `/api/v1/mail/inbound` with the `MAIL_INBOUND_WEBHOOK_SECRET`. `--demo` (or a yes to the
|
|
89
89
|
question after the apps) loads a sample company with contacts, products, orders, invoices and projects, made by
|
|
90
90
|
the demo users `dina`, `rahim`, `karim` and `sumi` (one shared password, printed at the end); not for production.
|
|
91
91
|
|
|
92
92
|
## Install with Docker
|
|
93
93
|
|
|
94
|
-
`
|
|
94
|
+
`shutro-erp init` writes `Dockerfile`, `compose.yaml`, `nginx.conf` and `.dockerignore` into every project.
|
|
95
95
|
|
|
96
96
|
**PostgreSQL in Docker, the app on your machine** (development):
|
|
97
97
|
|
|
@@ -117,21 +117,21 @@ docker compose up -d --build # db + api + web; the back-office on http://
|
|
|
117
117
|
| Service | What | Port |
|
|
118
118
|
|---|---|---|
|
|
119
119
|
| `db` | PostgreSQL 18, data in the `db` volume | `127.0.0.1:5432` (`POSTGRES_PORT`) |
|
|
120
|
-
| `api` | the
|
|
120
|
+
| `api` | the Shutro ERP API; runs `shutro-erp migrate` on every start, then serves | inside the network (`api:3000`) |
|
|
121
121
|
| `web` | nginx with the built back-office, `/api` proxied to `api` | `8080` (`WEB_PORT`) |
|
|
122
122
|
|
|
123
123
|
Useful commands:
|
|
124
124
|
|
|
125
125
|
```sh
|
|
126
126
|
docker compose logs -f api # the API's log
|
|
127
|
-
docker compose exec api npx
|
|
128
|
-
docker compose exec api npx
|
|
127
|
+
docker compose exec api npx shutro-erp migrate status # any shutro-erp command inside the container
|
|
128
|
+
docker compose exec api npx shutro-erp seed
|
|
129
129
|
docker compose up -d --build api # after changing modules: rebuild and migrate
|
|
130
130
|
docker compose down # stop (volumes keep the data)
|
|
131
|
-
docker compose exec db pg_dump -U
|
|
131
|
+
docker compose exec db pg_dump -U shutro my_erp > backup.sql # a backup
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
Behind HTTPS, set `
|
|
134
|
+
Behind HTTPS, set `SHUTRO_ERP_SECURE_COOKIES: "true"` in `compose.yaml`. To use a PostgreSQL server outside Docker, set
|
|
135
135
|
`DOCKER_DATABASE_URL` in `.env` (an address the container can reach, not `localhost`).
|
|
136
136
|
|
|
137
137
|
## Project structure
|
|
@@ -144,7 +144,7 @@ my-erp/
|
|
|
144
144
|
├── src/
|
|
145
145
|
│ ├── modules.ts the installed modules: base, the apps, your modules
|
|
146
146
|
│ ├── server.ts the API server (npm start)
|
|
147
|
-
│ ├── modules/ your modules (
|
|
147
|
+
│ ├── modules/ your modules (shutro-erp module create <name>)
|
|
148
148
|
│ │ └── library/
|
|
149
149
|
│ │ ├── index.ts the module: groups, access levels, app, menus, actions, views
|
|
150
150
|
│ │ ├── models.ts its models (fields, access per group, record rules)
|
|
@@ -152,7 +152,7 @@ my-erp/
|
|
|
152
152
|
│ │ └── migrations.ts its data migrations
|
|
153
153
|
│ └── web/
|
|
154
154
|
│ ├── main.tsx the back-office (React): renderers of each installed app
|
|
155
|
-
│ └── index.css Tailwind +
|
|
155
|
+
│ └── index.css Tailwind + Shutro ERP's design tokens
|
|
156
156
|
├── index.html, vite.config.ts the back-office build (npm run web / build:web)
|
|
157
157
|
├── Dockerfile, compose.yaml, nginx.conf, .dockerignore
|
|
158
158
|
└── var/attachments/ uploaded files (with folder storage)
|
|
@@ -163,8 +163,8 @@ my-erp/
|
|
|
163
163
|
A module is a folder with its models and a manifest. Create one:
|
|
164
164
|
|
|
165
165
|
```sh
|
|
166
|
-
npx
|
|
167
|
-
npx
|
|
166
|
+
npx shutro-erp module create library
|
|
167
|
+
npx shutro-erp migrate # creates its tables, records it, loads its seed data
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
It writes `src/modules/library/` and adds `library` to `src/modules.ts`. After `npm start` and a reload of the
|
|
@@ -198,7 +198,7 @@ export const Book = defineModel('library.book', {
|
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
**`index.ts`**: the module: its Shutro part (name, version, groups, models, seed data, data migrations) and its
|
|
201
|
-
|
|
201
|
+
Shutro ERP part (access levels, app, menus, actions, views):
|
|
202
202
|
|
|
203
203
|
```ts
|
|
204
204
|
import { base, baseShutro } from 'shutro-erp';
|
|
@@ -259,29 +259,29 @@ export const modules = [base, messaging, library];
|
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
```sh
|
|
262
|
-
npx
|
|
262
|
+
npx shutro-erp migrate # tables, the module's version (shutro_module), its seed data
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
A module published on npm installs the same way: `npm install <package>`, import it in `src/modules.ts`, then
|
|
266
|
-
`npx
|
|
266
|
+
`npx shutro-erp migrate`. Installing does not give anyone access: grant its roles to users ([Users and roles](#users-and-roles)).
|
|
267
267
|
|
|
268
|
-
**Remove a module**: take it out of `src/modules.ts`; `npx
|
|
269
|
-
destructive: it stops for review, and `npx
|
|
268
|
+
**Remove a module**: take it out of `src/modules.ts`; `npx shutro-erp migrate` then plans dropping its tables, which is
|
|
269
|
+
destructive: it stops for review, and `npx shutro-erp migrate --allow-destructive` applies it. Back up first.
|
|
270
270
|
|
|
271
271
|
## Migrations
|
|
272
272
|
|
|
273
273
|
```sh
|
|
274
|
-
npx
|
|
275
|
-
npx
|
|
276
|
-
npx
|
|
277
|
-
npx
|
|
278
|
-
npx
|
|
274
|
+
npx shutro-erp migrate # plan + apply + seed
|
|
275
|
+
npx shutro-erp migrate --plan # only write the plan, to review it
|
|
276
|
+
npx shutro-erp migrate --name add_isbn # name the new file (migrations/0002_add_isbn.sql)
|
|
277
|
+
npx shutro-erp migrate status # applied and pending files
|
|
278
|
+
npx shutro-erp migrate --allow-destructive
|
|
279
279
|
```
|
|
280
280
|
|
|
281
|
-
- **Schema migrations** are SQL files in `migrations/`. `
|
|
281
|
+
- **Schema migrations** are SQL files in `migrations/`. `shutro-erp migrate` compares your modules with the live database
|
|
282
282
|
and writes the differences as the next file (`0002_update.sql`), applies every pending file in order (each in its
|
|
283
283
|
own transaction, recorded in `shutro_migration`), then loads the seed data. Commit the files: a deployment runs the
|
|
284
|
-
same ones (the Docker `api` service runs `
|
|
284
|
+
same ones (the Docker `api` service runs `shutro-erp migrate` on start).
|
|
285
285
|
- **Destructive changes** (dropping a column or a table, narrowing a type) are written but not applied without
|
|
286
286
|
`--allow-destructive`. A file written earlier and not applied yet is applied first.
|
|
287
287
|
- **Per-module data migrations** change existing data when a module is upgraded. Declare them in the module's
|
|
@@ -305,7 +305,7 @@ export const migrations: DataMigration[] = [
|
|
|
305
305
|
|
|
306
306
|
```sh
|
|
307
307
|
# index.ts: version: '0.2.0'
|
|
308
|
-
npx
|
|
308
|
+
npx shutro-erp migrate # … module library: 0.1.0 → 0.2.0, data migration library@0.2.0
|
|
309
309
|
```
|
|
310
310
|
|
|
311
311
|
## Seed data
|
|
@@ -329,18 +329,18 @@ export const data = [
|
|
|
329
329
|
```
|
|
330
330
|
|
|
331
331
|
```sh
|
|
332
|
-
npx
|
|
333
|
-
npx
|
|
332
|
+
npx shutro-erp seed # every module's seed data, and the apps' defaults
|
|
333
|
+
npx shutro-erp seed library # one module (or several: shutro-erp seed library inventory)
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
`
|
|
336
|
+
`shutro-erp migrate` seeds too (skip with `--no-seed`). The apps' defaults are also seeded: approval policies
|
|
337
337
|
(permissions), document templates (reporting), activity types (messaging); users may edit them afterwards.
|
|
338
338
|
|
|
339
339
|
## Users and roles
|
|
340
340
|
|
|
341
341
|
```sh
|
|
342
|
-
npx
|
|
343
|
-
npx
|
|
342
|
+
npx shutro-erp user create rahim --name "Rahim Uddin" --email rahim@example.com --roles library.user,messaging.user
|
|
343
|
+
npx shutro-erp user password rahim # a new password (asked, hidden)
|
|
344
344
|
```
|
|
345
345
|
|
|
346
346
|
Roles are the groups of the installed modules' access levels (`library.user`, `library.manager`,
|
|
@@ -354,31 +354,31 @@ their roles grants it. Administrators manage roles, field security and grants in
|
|
|
354
354
|
|---|---|
|
|
355
355
|
| `DATABASE_URL` | PostgreSQL: `postgres://user:password@host:5432/database` |
|
|
356
356
|
| `PORT`, `HOST` | the API's port (3000) and address (127.0.0.1; 0.0.0.0 in Docker) |
|
|
357
|
-
| `
|
|
357
|
+
| `SHUTRO_ERP_STORAGE_DIR` | attachments in this folder, or: |
|
|
358
358
|
| `S3_ENDPOINT`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_REGION` | S3-compatible storage |
|
|
359
|
-
| `
|
|
359
|
+
| `SHUTRO_ERP_ATTACHMENT_MAX_BYTES` | upload limit (25 MB) |
|
|
360
360
|
| `VALKEY_URL` | optional Valkey/Redis: rate limits and caches across servers |
|
|
361
|
-
| `
|
|
362
|
-
| `
|
|
361
|
+
| `SHUTRO_ERP_SECURE_COOKIES` | `true` behind HTTPS (default in production), `false` for plain HTTP |
|
|
362
|
+
| `SHUTRO_ERP_SCHEDULES` | `off` stops the scheduled-report ticker on this server |
|
|
363
363
|
| `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_PORT` | the Docker `db` service |
|
|
364
364
|
| `DOCKER_DATABASE_URL`, `WEB_PORT` | Docker: another database for `api`; the back-office's port (8080) |
|
|
365
365
|
|
|
366
366
|
## Command reference
|
|
367
367
|
|
|
368
|
-
Run them in the project folder (`npx
|
|
368
|
+
Run them in the project folder (`npx shutro-erp …`, or `npm run migrate` / `npm run seed`). They read `.env` and
|
|
369
369
|
`src/modules.ts` (`--modules <file>` for another list).
|
|
370
370
|
|
|
371
371
|
| Command | What |
|
|
372
372
|
|---|---|
|
|
373
|
-
| `
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
377
|
-
| `
|
|
378
|
-
| `
|
|
379
|
-
| `
|
|
380
|
-
| `
|
|
381
|
-
| `
|
|
373
|
+
| `shutro-erp init [folder]` | set up an installation (questions, or flags with `--yes`); again later to enrol 2FA |
|
|
374
|
+
| `shutro-erp migrate` | apply pending files, plan what is missing into a new file and apply it, run data migrations, seed |
|
|
375
|
+
| `shutro-erp migrate --plan` · `--name <n>` · `--allow-destructive` · `--no-seed` | review only · file name · drops · no seeding |
|
|
376
|
+
| `shutro-erp migrate status` | applied and pending migration files |
|
|
377
|
+
| `shutro-erp seed [module …]` | seed data of all modules, or of those named |
|
|
378
|
+
| `shutro-erp module create <name>` | a new module in `src/modules/<name>/`, listed in `src/modules.ts` |
|
|
379
|
+
| `shutro-erp user create <login>` | a user: `--name`, `--email`, `--roles`, `--password` (asked when missing) |
|
|
380
|
+
| `shutro-erp user password <login>` | a new password |
|
|
381
|
+
| `shutro-erp generate client` | a typed API client for your front-end (`--out src/api/shutro-erp.gen.ts`) |
|
|
382
382
|
| `npm start` · `npm run web` · `npm run build:web` | the API · the back-office (dev) · the back-office build (`dist/`) |
|
|
383
383
|
| `npm run docker:up` | `docker compose up -d --build` |
|
|
384
384
|
|
|
@@ -387,26 +387,26 @@ Run them in the project folder (`npx kosh …`, or `npm run migrate` / `npm run
|
|
|
387
387
|
| Import | What |
|
|
388
388
|
|---|---|
|
|
389
389
|
| `shutro-erp` | the server: `createServer`, `startServer`, `base`, REST + OpenAPI, sessions and 2FA |
|
|
390
|
-
| `shutro-erp/orm` | Shutro (models, fields, groups, records, migrations) as
|
|
390
|
+
| `shutro-erp/orm` | Shutro (models, fields, groups, records, migrations) as Shutro ERP uses it |
|
|
391
391
|
| `shutro-erp/modules-sdk` | `defineModule`: manifests, access levels, menus, actions |
|
|
392
392
|
| `shutro-erp/views`, `shutro-erp/search` | view definitions (`list`, `form`, `kanban`, `pivot`, …) and the search layer |
|
|
393
393
|
| `shutro-erp/permissions` | roles, field security, approvals (`/web`: the approval panel) |
|
|
394
394
|
| `shutro-erp/reporting` | pivots, exports, documents, scheduled reports (`/web`: the report views) |
|
|
395
395
|
| `shutro-erp/messaging`, `shutro-erp/storage` | the chatter, activities, attachments (`/web`: the chatter) |
|
|
396
396
|
| `shutro-erp/ui` | the React app shell, design system and renderers; `shutro-erp/ui/styles.css` the tokens |
|
|
397
|
-
| `shutro-erp/cli` | the `
|
|
397
|
+
| `shutro-erp/cli` | the `shutro-erp` command's functions |
|
|
398
398
|
|
|
399
399
|
## Troubleshooting
|
|
400
400
|
|
|
401
401
|
- **"PostgreSQL did not answer"**: nothing listens at that host and port. Start your PostgreSQL service, or
|
|
402
402
|
answer `2` (Docker) to the first question; Docker Desktop must be running.
|
|
403
403
|
- **"password authentication failed"**: the user or password in `DATABASE_URL` is wrong.
|
|
404
|
-
- **Signed in but no apps**: the user has no role granting them (`
|
|
405
|
-
their authenticator enrolled (`npx
|
|
406
|
-
- **Sign-in works on the API but not in the browser over plain HTTP**: `
|
|
407
|
-
- **Lost the phone**: sign in with one of the recovery codes `
|
|
408
|
-
- **"It drops or changes existing data"**: `
|
|
409
|
-
`migrations/`, back up, then `npx
|
|
404
|
+
- **Signed in but no apps**: the user has no role granting them (`shutro-erp user create … --roles`); administrators need
|
|
405
|
+
their authenticator enrolled (`npx shutro-erp init` again).
|
|
406
|
+
- **Sign-in works on the API but not in the browser over plain HTTP**: `SHUTRO_ERP_SECURE_COOKIES=false`.
|
|
407
|
+
- **Lost the phone**: sign in with one of the recovery codes `shutro-erp init` printed.
|
|
408
|
+
- **"It drops or changes existing data"**: `shutro-erp migrate` found a destructive change; read the new file in
|
|
409
|
+
`migrations/`, back up, then `npx shutro-erp migrate --allow-destructive`.
|
|
410
410
|
|
|
411
411
|
Documentation, design notes and the source: https://github.com/isahaq1/kosh
|
|
412
412
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash:
|
|
1
|
+
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 0ea5d21ac5b24166
|
|
2
2
|
// Bindings of module `account_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,4 +1,4 @@
|
|
|
1
|
-
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash:
|
|
1
|
+
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 0ea5d21ac5b24166
|
|
2
2
|
// Schema view of module `account_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
|
|
|
@@ -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;
|
|
@@ -582,6 +583,34 @@ export interface AuditEvent {
|
|
|
582
583
|
export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
|
|
583
584
|
}
|
|
584
585
|
|
|
586
|
+
/** `auth.api_key` (owner: base). */
|
|
587
|
+
export interface AuthApiKey {
|
|
588
|
+
readonly name: 'auth.api_key';
|
|
589
|
+
readonly fields: {
|
|
590
|
+
readonly id: F.Id<'auth.api_key'>;
|
|
591
|
+
readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
|
|
592
|
+
readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
|
|
593
|
+
readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
|
|
594
|
+
readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
|
|
595
|
+
readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
|
|
596
|
+
readonly name: F.Char<{ readonly required: true }>;
|
|
597
|
+
readonly user: F.Many2one<'res.users', { readonly required: true }>;
|
|
598
|
+
readonly tokenHash: F.Char<{ readonly required: true }>;
|
|
599
|
+
readonly prefix: F.Char<{ readonly required: true }>;
|
|
600
|
+
readonly scopes: F.Text<{ readonly required: true }>;
|
|
601
|
+
readonly expiresAt: F.Datetime<{ readonly required: true }>;
|
|
602
|
+
readonly lastUsedAt: F.Datetime;
|
|
603
|
+
readonly lastUsedIp: F.Char;
|
|
604
|
+
readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
|
|
605
|
+
readonly issuedBy: F.Many2one<'res.users'>;
|
|
606
|
+
};
|
|
607
|
+
readonly methods: AuthApiKeyMethods;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** Methods of `auth.api_key`: declared ones and transitions. */
|
|
611
|
+
export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
|
|
612
|
+
}
|
|
613
|
+
|
|
585
614
|
/** `auth.credential` (owner: base). */
|
|
586
615
|
export interface AuthCredential {
|
|
587
616
|
readonly name: 'auth.credential';
|
|
@@ -1292,6 +1321,7 @@ export interface ResUsers {
|
|
|
1292
1321
|
readonly companies: F.Many2many<'res.company', { readonly required: true }>;
|
|
1293
1322
|
readonly lang: F.Char<{ readonly default: true }>;
|
|
1294
1323
|
readonly tz: F.Char<{ readonly default: true }>;
|
|
1324
|
+
readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
|
|
1295
1325
|
readonly portalPartner: F.Many2one<'res.partner'>; // portal
|
|
1296
1326
|
};
|
|
1297
1327
|
readonly methods: ResUsersMethods;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* `shutro-erp/account_portal`: Accounting in the portal (RFC 0015 §5.4). Proposed whenever Accounting and the portal
|
|
4
4
|
* are installed. Customers see their posted invoices and credit notes, what is still due, print them and write on
|
|
5
|
-
* them; vendors see the bills recorded for them.
|
|
6
|
-
*
|
|
5
|
+
* them; vendors see the bills recorded for them. Open invoices are payable online when the `payment` module and
|
|
6
|
+
* a provider are installed (RFC 0017 §5.5); otherwise by the payment details on the invoice.
|
|
7
7
|
*/
|
|
8
8
|
import { accounting } from '../accounting/index.js';
|
|
9
9
|
import { defineModule } from '../modules-sdk/index.js';
|
|
@@ -55,6 +55,7 @@ export const accountPortal = defineModule({
|
|
|
55
55
|
thread: true,
|
|
56
56
|
attention: { kind: 'out_invoice', paymentState: { in: ['not_paid', 'partial'] } },
|
|
57
57
|
announce: { state: 'posted' },
|
|
58
|
+
payment: { when: { kind: 'out_invoice', paymentState: { in: ['not_paid', 'partial'] } } },
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
61
|
key: 'bills',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash:
|
|
1
|
+
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: c83d584195f8dd43
|
|
2
2
|
// Bindings of module `accounting`: 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:
|
|
1
|
+
// GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: c83d584195f8dd43
|
|
2
2
|
// Schema view of module `accounting` (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;
|
|
@@ -579,6 +580,34 @@ export interface AuditEvent {
|
|
|
579
580
|
export interface AuditEventMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'audit.event'> {
|
|
580
581
|
}
|
|
581
582
|
|
|
583
|
+
/** `auth.api_key` (owner: base). */
|
|
584
|
+
export interface AuthApiKey {
|
|
585
|
+
readonly name: 'auth.api_key';
|
|
586
|
+
readonly fields: {
|
|
587
|
+
readonly id: F.Id<'auth.api_key'>;
|
|
588
|
+
readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
|
|
589
|
+
readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
|
|
590
|
+
readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
|
|
591
|
+
readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
|
|
592
|
+
readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
|
|
593
|
+
readonly name: F.Char<{ readonly required: true }>;
|
|
594
|
+
readonly user: F.Many2one<'res.users', { readonly required: true }>;
|
|
595
|
+
readonly tokenHash: F.Char<{ readonly required: true }>;
|
|
596
|
+
readonly prefix: F.Char<{ readonly required: true }>;
|
|
597
|
+
readonly scopes: F.Text<{ readonly required: true }>;
|
|
598
|
+
readonly expiresAt: F.Datetime<{ readonly required: true }>;
|
|
599
|
+
readonly lastUsedAt: F.Datetime;
|
|
600
|
+
readonly lastUsedIp: F.Char;
|
|
601
|
+
readonly revoked: F.Bool<{ readonly required: true; readonly default: true }>;
|
|
602
|
+
readonly issuedBy: F.Many2one<'res.users'>;
|
|
603
|
+
};
|
|
604
|
+
readonly methods: AuthApiKeyMethods;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** Methods of `auth.api_key`: declared ones and transitions. */
|
|
608
|
+
export interface AuthApiKeyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.api_key'> {
|
|
609
|
+
}
|
|
610
|
+
|
|
582
611
|
/** `auth.credential` (owner: base). */
|
|
583
612
|
export interface AuthCredential {
|
|
584
613
|
readonly name: 'auth.credential';
|
|
@@ -1216,6 +1245,7 @@ export interface ResUsers {
|
|
|
1216
1245
|
readonly companies: F.Many2many<'res.company', { readonly required: true }>;
|
|
1217
1246
|
readonly lang: F.Char<{ readonly default: true }>;
|
|
1218
1247
|
readonly tz: F.Char<{ readonly default: true }>;
|
|
1248
|
+
readonly isService: F.Bool<{ readonly required: true; readonly default: true }>;
|
|
1219
1249
|
};
|
|
1220
1250
|
readonly methods: ResUsersMethods;
|
|
1221
1251
|
}
|
package/dist/accounting/index.js
CHANGED
|
@@ -404,6 +404,10 @@ export const accounting = defineModule({
|
|
|
404
404
|
sequence: 5,
|
|
405
405
|
},
|
|
406
406
|
],
|
|
407
|
+
// What webhooks may subscribe to besides record changes (RFC 0017 §5.3).
|
|
408
|
+
events: [
|
|
409
|
+
{ topic: 'account.move.posted', model: 'account.move', label: 'Entry, invoice or bill posted' },
|
|
410
|
+
],
|
|
407
411
|
approvals: [
|
|
408
412
|
{
|
|
409
413
|
name: 'Vendor bill approvals',
|
package/dist/assistant/routes.js
CHANGED
|
@@ -209,7 +209,7 @@ export function registerAssistantRoutes(context, options) {
|
|
|
209
209
|
? 'No API key is configured on this server (ANTHROPIC_API_KEY).'
|
|
210
210
|
: 'An administrator has not switched the assistant on for this company.');
|
|
211
211
|
if ((await usedThisMonth(env.companyId)) >= options.monthlyTokens)
|
|
212
|
-
throw new HttpProblem(429, 'assistant-budget', "The month's assistant budget is used up", `This company used its ${options.monthlyTokens} tokens of the month.`, 'An administrator can raise
|
|
212
|
+
throw new HttpProblem(429, 'assistant-budget', "The month's assistant budget is used up", `This company used its ${options.monthlyTokens} tokens of the month.`, 'An administrator can raise SHUTRO_ERP_ASSISTANT_MONTHLY_TOKENS.');
|
|
213
213
|
return client;
|
|
214
214
|
};
|
|
215
215
|
/** Logs a call in a transaction of its own (after the model answered: no transaction stays open meanwhile). */
|
|
@@ -832,7 +832,7 @@ export function webhookProblem(url, hosts = webhookHosts()) {
|
|
|
832
832
|
return 'Webhooks call https URLs (http only for localhost).';
|
|
833
833
|
const allowed = hosts.some((h) => (h.startsWith('*.') ? host.endsWith(h.slice(1)) : host === h));
|
|
834
834
|
if (!allowed)
|
|
835
|
-
return `The host ${host} is not on the webhook allow-list (
|
|
835
|
+
return `The host ${host} is not on the webhook allow-list (SHUTRO_ERP_AUTOMATION_WEBHOOK_HOSTS).`;
|
|
836
836
|
return null;
|
|
837
837
|
}
|
|
838
838
|
/** The signature of a webhook body: `sha256=<hex HMAC>` with the rule's secret. */
|
|
@@ -62,7 +62,7 @@ export const messages = {
|
|
|
62
62
|
cronHint: 'Minute hour day month weekday: "0 2 * * *" is every night at 02:00; @daily, @weekly, @monthly too. Runs on the records the condition matches.',
|
|
63
63
|
url: 'URL',
|
|
64
64
|
webhookFields: 'Fields to send',
|
|
65
|
-
webhookHint: "A signed JSON POST (X-Kosh-Signature: sha256 HMAC with the rule's webhook secret), retried on failure; the host must be on the allow-list (
|
|
65
|
+
webhookHint: "A signed JSON POST (X-Kosh-Signature: sha256 HMAC with the rule's webhook secret), retried on failure; the host must be on the allow-list (SHUTRO_ERP_AUTOMATION_WEBHOOK_HOSTS).",
|
|
66
66
|
condition: 'And the record matches',
|
|
67
67
|
conditionJson: 'Condition (domain)',
|
|
68
68
|
field: 'Field',
|
|
@@ -169,7 +169,7 @@ export const messages = {
|
|
|
169
169
|
cronHint: 'মিনিট ঘণ্টা দিন মাস সপ্তাহের দিন: "0 2 * * *" মানে প্রতি রাত ০২:০০; @daily, @weekly, @monthly-ও চলে। শর্তে মেলা রেকর্ডে চলে।',
|
|
170
170
|
url: 'URL',
|
|
171
171
|
webhookFields: 'যে ফিল্ড পাঠাবে',
|
|
172
|
-
webhookHint: 'স্বাক্ষরিত JSON POST (X-Kosh-Signature: নিয়মের ওয়েবহুক সিক্রেট দিয়ে sha256 HMAC), ব্যর্থ হলে আবার চেষ্টা; হোস্ট অনুমোদিত তালিকায় থাকতে হবে (
|
|
172
|
+
webhookHint: 'স্বাক্ষরিত JSON POST (X-Kosh-Signature: নিয়মের ওয়েবহুক সিক্রেট দিয়ে sha256 HMAC), ব্যর্থ হলে আবার চেষ্টা; হোস্ট অনুমোদিত তালিকায় থাকতে হবে (SHUTRO_ERP_AUTOMATION_WEBHOOK_HOSTS)।',
|
|
173
173
|
condition: 'এবং রেকর্ড মেলে',
|
|
174
174
|
conditionJson: 'শর্ত (ডোমেইন)',
|
|
175
175
|
field: 'ফিল্ড',
|
package/dist/cli/apps.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export interface AppsIo {
|
|
|
8
8
|
/** The confirmation question (tests script it). Default: the terminal. */
|
|
9
9
|
readonly prompter?: Prompter;
|
|
10
10
|
}
|
|
11
|
-
/** Runs `
|
|
11
|
+
/** Runs `shutro-erp apps …`. Returns the exit code. */
|
|
12
12
|
export declare function runApps(argv: readonly string[], io: AppsIo): Promise<number>;
|
package/dist/cli/apps.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `
|
|
2
|
+
* `shutro-erp apps` (RFC 0008): the App manager in the terminal. Lists the catalogue with each app's state, and installs,
|
|
3
3
|
* enables or disables apps: the plan is shown first (modules, dependencies, cascades, schema statements), then
|
|
4
4
|
* applied in one transaction by `shutro-erp` (`prepareApps` / `applyApps`).
|
|
5
5
|
*/
|
|
@@ -13,14 +13,14 @@ import { PROTECTED_APPS } from '../settings/index.js';
|
|
|
13
13
|
import { modulesOf, seedWith } from './project.js';
|
|
14
14
|
import { defaultsPrompter, terminalPrompter } from './prompt.js';
|
|
15
15
|
import { createUi, PLAIN_TERMINAL } from './ui.js';
|
|
16
|
-
export const APPS_USAGE = `Usage:
|
|
16
|
+
export const APPS_USAGE = `Usage: shutro-erp apps [command] [app …] [options]
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
shutro-erp apps the catalogue: installed, disabled and available apps
|
|
19
|
+
shutro-erp apps install <app …> install apps and what they need, in one transaction
|
|
20
|
+
shutro-erp apps enable <app …> serve disabled apps again (their data is still there)
|
|
21
|
+
shutro-erp apps disable <app …> switch apps off: menus, API and access go, tables and data stay
|
|
22
22
|
(apps that depend on them are disabled too)
|
|
23
|
-
|
|
23
|
+
shutro-erp apps upgrade [app …] move installed apps to their code's version: schema, data migrations,
|
|
24
24
|
seed data (default: every app whose code is newer)
|
|
25
25
|
|
|
26
26
|
Options:
|
|
@@ -32,7 +32,7 @@ Options:
|
|
|
32
32
|
|
|
33
33
|
After a change, restart the API (npm start) so it serves the new set.
|
|
34
34
|
`;
|
|
35
|
-
/** Pictographs for the apps
|
|
35
|
+
/** Pictographs for the apps Shutro ERP ships; other modules get a puzzle piece. */
|
|
36
36
|
const APP_ICONS = {
|
|
37
37
|
base: '🏠',
|
|
38
38
|
settings: '⚙️',
|
|
@@ -74,7 +74,7 @@ async function list(ui, driver, catalogue) {
|
|
|
74
74
|
const label = m.app?.label ?? '';
|
|
75
75
|
const why = row?.reason === 'dependency' ? ui.style('gray', ' (needed by another app)') : '';
|
|
76
76
|
const behind = row !== undefined && row.version !== m.version
|
|
77
|
-
? ui.style('cyan', ` (installed ${row.version}:
|
|
77
|
+
? ui.style('cyan', ` (installed ${row.version}: shutro-erp apps upgrade ${m.name})`)
|
|
78
78
|
: '';
|
|
79
79
|
ui.write(` ${marks[s]} ${iconOf(ui, m.name)} ${ui.style('bold', m.name.padEnd(width))} ${ui.style('gray', m.version.padEnd(8))} ${s.padEnd(9)} ${label}${why}${behind}\n`);
|
|
80
80
|
}
|
|
@@ -103,7 +103,7 @@ function showPlan(ui, prepared, catalogue) {
|
|
|
103
103
|
if (plan.disable.length > 0)
|
|
104
104
|
ui.detail('tables and data stay; enable brings everything back');
|
|
105
105
|
}
|
|
106
|
-
/** Runs `
|
|
106
|
+
/** Runs `shutro-erp apps …`. Returns the exit code. */
|
|
107
107
|
export async function runApps(argv, io) {
|
|
108
108
|
const { values, positionals } = parseArgs({
|
|
109
109
|
args: [...argv],
|
|
@@ -131,11 +131,11 @@ export async function runApps(argv, io) {
|
|
|
131
131
|
}
|
|
132
132
|
if (command === 'uninstall') {
|
|
133
133
|
ui.warn('Removing an app and its data comes with a later step of RFC 0008.');
|
|
134
|
-
ui.info(`Meanwhile:
|
|
134
|
+
ui.info(`Meanwhile: shutro-erp apps disable ${names.join(' ') || '<app>'} (reversible, keeps the data).`);
|
|
135
135
|
return 2;
|
|
136
136
|
}
|
|
137
137
|
if (command !== 'list' && command !== 'upgrade' && names.length === 0) {
|
|
138
|
-
ui.fail(`Name the app(s):
|
|
138
|
+
ui.fail(`Name the app(s): shutro-erp apps ${command} <app …>`);
|
|
139
139
|
return 2;
|
|
140
140
|
}
|
|
141
141
|
const envFile = join(io.cwd, '.env');
|
|
@@ -143,14 +143,14 @@ export async function runApps(argv, io) {
|
|
|
143
143
|
process.loadEnvFile(envFile);
|
|
144
144
|
const databaseUrl = process.env['DATABASE_URL'];
|
|
145
145
|
if (databaseUrl === undefined || databaseUrl === '') {
|
|
146
|
-
ui.fail('DATABASE_URL is not set: run
|
|
146
|
+
ui.fail('DATABASE_URL is not set: run shutro-erp init, or add it to .env.');
|
|
147
147
|
return 1;
|
|
148
148
|
}
|
|
149
149
|
const driver = pg.createDriver({
|
|
150
150
|
url: databaseUrl,
|
|
151
151
|
max: 2,
|
|
152
152
|
parseNumeric: orm.toDecimal,
|
|
153
|
-
applicationName: '
|
|
153
|
+
applicationName: 'shutro-erp apps',
|
|
154
154
|
});
|
|
155
155
|
const prompter = io.prompter ??
|
|
156
156
|
(values.yes === true || values.plan === true ? defaultsPrompter() : terminalPrompter());
|
package/dist/cli/bin.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { applyEnvironmentNames } from '../server/index.js';
|
|
2
3
|
import { runCli } from './index.js';
|
|
3
4
|
import { detectTerminal } from './ui.js';
|
|
5
|
+
// SHUTRO_ERP_* settings, read under their older KOSH_* names.
|
|
6
|
+
applyEnvironmentNames();
|
|
4
7
|
process.exitCode = await runCli(process.argv.slice(2), (text) => process.stdout.write(text), {
|
|
5
8
|
terminal: detectTerminal(process.stdout),
|
|
6
9
|
});
|