shutro-erp 0.3.1 → 0.3.3

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 (168) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +29 -1
  3. package/dist/account_portal/index.js +3 -2
  4. package/dist/accounting/.kosh/bindings.js +1 -1
  5. package/dist/accounting/.kosh/schema.d.ts +29 -1
  6. package/dist/accounting/index.js +4 -0
  7. package/dist/assistant/index.js +1 -1
  8. package/dist/cli/init.js +2 -0
  9. package/dist/cli/launch.js +8 -5
  10. package/dist/cli/templates.js +4 -1
  11. package/dist/cli/ui.js +19 -1
  12. package/dist/contacts/.kosh/bindings.js +1 -1
  13. package/dist/contacts/.kosh/schema.d.ts +29 -1
  14. package/dist/integrations/inbound.d.ts +36 -0
  15. package/dist/integrations/inbound.js +171 -0
  16. package/dist/integrations/index.d.ts +18 -0
  17. package/dist/integrations/index.js +400 -0
  18. package/dist/integrations/models.d.ts +6 -0
  19. package/dist/integrations/models.js +119 -0
  20. package/dist/integrations/web/DeliveryTools.d.ts +4 -0
  21. package/dist/integrations/web/DeliveryTools.js +15 -0
  22. package/dist/integrations/web/EndpointTools.d.ts +7 -0
  23. package/dist/integrations/web/EndpointTools.js +25 -0
  24. package/dist/integrations/web/InboundTools.d.ts +4 -0
  25. package/dist/integrations/web/InboundTools.js +15 -0
  26. package/dist/integrations/web/WebhookTools.d.ts +7 -0
  27. package/dist/integrations/web/WebhookTools.js +25 -0
  28. package/dist/integrations/web/index.d.ts +16 -0
  29. package/dist/integrations/web/index.js +27 -0
  30. package/dist/integrations/web/messages.d.ts +2 -0
  31. package/dist/integrations/web/messages.js +53 -0
  32. package/dist/integrations/webhooks.d.ts +63 -0
  33. package/dist/integrations/webhooks.js +217 -0
  34. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  35. package/dist/l10n_bd/.kosh/schema.d.ts +29 -1
  36. package/dist/l10n_in/.kosh/bindings.js +1 -1
  37. package/dist/l10n_in/.kosh/schema.d.ts +29 -4
  38. package/dist/l10n_in/module.d.ts +0 -62
  39. package/dist/l10n_in/module.js +2 -4
  40. package/dist/modules-sdk/index.d.ts +1 -1
  41. package/dist/modules-sdk/manifest.d.ts +48 -0
  42. package/dist/payment/.kosh/bindings.d.ts +22 -0
  43. package/dist/payment/.kosh/bindings.js +15 -0
  44. package/dist/payment/.kosh/schema.d.ts +1581 -0
  45. package/dist/payment/bkash.d.ts +28 -0
  46. package/dist/payment/bkash.js +169 -0
  47. package/dist/payment/index.d.ts +38 -0
  48. package/dist/payment/index.js +431 -0
  49. package/dist/payment/module.d.ts +6856 -0
  50. package/dist/payment/module.js +77 -0
  51. package/dist/payment/money.d.ts +7 -0
  52. package/dist/payment/money.js +37 -0
  53. package/dist/payment/providers.d.ts +75 -0
  54. package/dist/payment/providers.js +77 -0
  55. package/dist/payment/razorpay.d.ts +12 -0
  56. package/dist/payment/razorpay.js +149 -0
  57. package/dist/payment/settle.d.ts +11 -0
  58. package/dist/payment/settle.js +59 -0
  59. package/dist/payment/sslcommerz.d.ts +12 -0
  60. package/dist/payment/sslcommerz.js +185 -0
  61. package/dist/payment/stripe.d.ts +15 -0
  62. package/dist/payment/stripe.js +182 -0
  63. package/dist/permissions/admin.d.ts +3 -0
  64. package/dist/permissions/admin.js +108 -10
  65. package/dist/permissions/index.d.ts +1 -0
  66. package/dist/permissions/index.js +3 -0
  67. package/dist/permissions/roles.d.ts +2 -0
  68. package/dist/permissions/roles.js +236 -0
  69. package/dist/portal/.kosh/bindings.js +1 -1
  70. package/dist/portal/.kosh/schema.d.ts +29 -1
  71. package/dist/portal/routes-documents.js +5 -0
  72. package/dist/product/.kosh/bindings.js +1 -1
  73. package/dist/product/.kosh/schema.d.ts +29 -1
  74. package/dist/project/.kosh/bindings.js +1 -1
  75. package/dist/project/.kosh/schema.d.ts +29 -1
  76. package/dist/project_portal/.kosh/bindings.js +1 -1
  77. package/dist/project_portal/.kosh/schema.d.ts +29 -1
  78. package/dist/purchase/.kosh/bindings.js +1 -1
  79. package/dist/purchase/.kosh/schema.d.ts +29 -1
  80. package/dist/purchase_account/.kosh/bindings.js +1 -1
  81. package/dist/purchase_account/.kosh/schema.d.ts +29 -1
  82. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  83. package/dist/purchase_portal/.kosh/schema.d.ts +29 -1
  84. package/dist/sale_account/.kosh/bindings.js +1 -1
  85. package/dist/sale_account/.kosh/schema.d.ts +29 -1
  86. package/dist/sale_portal/.kosh/bindings.js +1 -1
  87. package/dist/sale_portal/.kosh/schema.d.ts +29 -1
  88. package/dist/sale_project/.kosh/bindings.js +1 -1
  89. package/dist/sale_project/.kosh/schema.d.ts +29 -1
  90. package/dist/sales/.kosh/bindings.js +1 -1
  91. package/dist/sales/.kosh/schema.d.ts +29 -1
  92. package/dist/sales/index.js +2 -0
  93. package/dist/server/api/rest.d.ts +6 -1
  94. package/dist/server/api/rest.js +11 -0
  95. package/dist/server/auth/base.js +22 -0
  96. package/dist/server/auth/routes.d.ts +3 -0
  97. package/dist/server/auth/routes.js +29 -0
  98. package/dist/server/auth/service.d.ts +52 -0
  99. package/dist/server/auth/service.js +168 -3
  100. package/dist/server/identity.d.ts +2 -0
  101. package/dist/server/index.d.ts +2 -2
  102. package/dist/server/index.js +1 -1
  103. package/dist/server/server.js +11 -3
  104. package/dist/settings/company.d.ts +34 -0
  105. package/dist/settings/company.js +187 -0
  106. package/dist/settings/index.d.ts +10 -10
  107. package/dist/settings/index.js +159 -30
  108. package/dist/settings/people.d.ts +16 -0
  109. package/dist/settings/people.js +505 -0
  110. package/dist/settings/security.d.ts +13 -0
  111. package/dist/settings/security.js +158 -0
  112. package/dist/settings/web/AuditPage.d.ts +8 -0
  113. package/dist/settings/web/AuditPage.js +74 -0
  114. package/dist/settings/web/CompaniesPage.d.ts +8 -0
  115. package/dist/settings/web/CompaniesPage.js +52 -0
  116. package/dist/settings/web/CompanyPage.d.ts +9 -0
  117. package/dist/settings/web/CompanyPage.js +94 -0
  118. package/dist/settings/web/OverviewPage.d.ts +9 -0
  119. package/dist/settings/web/OverviewPage.js +66 -0
  120. package/dist/settings/web/PeoplePage.d.ts +9 -0
  121. package/dist/settings/web/PeoplePage.js +163 -0
  122. package/dist/settings/web/ProfilePage.d.ts +14 -0
  123. package/dist/settings/web/ProfilePage.js +194 -0
  124. package/dist/settings/web/RolesEditor.d.ts +35 -0
  125. package/dist/settings/web/RolesEditor.js +49 -0
  126. package/dist/settings/web/RolesPage.d.ts +9 -0
  127. package/dist/settings/web/RolesPage.js +296 -0
  128. package/dist/settings/web/SecurityPage.d.ts +8 -0
  129. package/dist/settings/web/SecurityPage.js +81 -0
  130. package/dist/settings/web/index.d.ts +9 -0
  131. package/dist/settings/web/index.js +26 -1
  132. package/dist/settings/web/messages.js +566 -0
  133. package/dist/ui/api.d.ts +4 -0
  134. package/dist/ui/app.d.ts +5 -0
  135. package/dist/ui/app.js +108 -31
  136. package/dist/ui/components/primitives.d.ts +9 -1
  137. package/dist/ui/components/primitives.js +12 -4
  138. package/dist/ui/context.d.ts +10 -1
  139. package/dist/ui/context.js +60 -2
  140. package/dist/ui/i18n/messages.d.ts +46 -0
  141. package/dist/ui/i18n/messages.js +92 -0
  142. package/dist/ui/index.d.ts +2 -1
  143. package/dist/ui/index.js +2 -1
  144. package/dist/ui/lib/accent.d.ts +13 -0
  145. package/dist/ui/lib/accent.js +36 -0
  146. package/dist/ui/shell/Activate.d.ts +6 -0
  147. package/dist/ui/shell/Activate.js +39 -0
  148. package/dist/ui/shell/CommandPalette.d.ts +7 -1
  149. package/dist/ui/shell/CommandPalette.js +16 -2
  150. package/dist/ui/shell/Sidebar.d.ts +27 -0
  151. package/dist/ui/shell/Sidebar.js +84 -0
  152. package/dist/ui/shell/TwoFactorSetup.d.ts +5 -0
  153. package/dist/ui/shell/TwoFactorSetup.js +32 -0
  154. package/dist/ui/shell/shortcuts.d.ts +3 -0
  155. package/dist/ui/shell/shortcuts.js +13 -4
  156. package/dist/ui/styles.css +70 -28
  157. package/dist/ui/views/DateField.d.ts +10 -0
  158. package/dist/ui/views/DateField.js +129 -0
  159. package/dist/ui/views/FormView.js +161 -21
  160. package/dist/ui/views/ListView.d.ts +2 -0
  161. package/dist/ui/views/ListView.js +8 -3
  162. package/dist/ui/views/StatusChip.d.ts +10 -0
  163. package/dist/ui/views/StatusChip.js +22 -0
  164. package/dist/ui/views/widgets.d.ts +3 -0
  165. package/dist/ui/views/widgets.js +26 -8
  166. package/dist/views/types.d.ts +7 -0
  167. package/dist/views/validate.js +4 -0
  168. package/package.json +14 -2
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 64da9b36804c7c1a
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2bbbe12acf870fd9
2
2
  // Schema view of module `purchase_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
 
@@ -11,6 +11,7 @@ export interface SchemaView {
11
11
  readonly 'audit.event': AuditEvent;
12
12
  readonly 'auth.api_key': AuthApiKey;
13
13
  readonly 'auth.credential': AuthCredential;
14
+ readonly 'auth.policy': AuthPolicy;
14
15
  readonly 'auth.session': AuthSession;
15
16
  readonly 'auth.user_grant': AuthUserGrant;
16
17
  readonly 'auth.user_group': AuthUserGroup;
@@ -222,6 +223,26 @@ export interface AuthCredential {
222
223
  export interface AuthCredentialMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.credential'> {
223
224
  }
224
225
 
226
+ /** `auth.policy` (owner: base). */
227
+ export interface AuthPolicy {
228
+ readonly name: 'auth.policy';
229
+ readonly fields: {
230
+ readonly id: F.Id<'auth.policy'>;
231
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
232
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
233
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
234
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
235
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
236
+ readonly twoFactor: F.Selection<'administrators' | 'everyone', { readonly required: true; readonly default: true }>;
237
+ readonly sessionDays: F.Integer;
238
+ };
239
+ readonly methods: AuthPolicyMethods;
240
+ }
241
+
242
+ /** Methods of `auth.policy`: declared ones and transitions. */
243
+ export interface AuthPolicyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.policy'> {
244
+ }
245
+
225
246
  /** `auth.session` (owner: base). */
226
247
  export interface AuthSession {
227
248
  readonly name: 'auth.session';
@@ -706,6 +727,13 @@ export interface ResCompany {
706
727
  readonly name: F.Char<{ readonly required: true }>;
707
728
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
708
729
  readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
730
+ readonly accentColor: F.Char;
731
+ readonly street: F.Char;
732
+ readonly city: F.Char;
733
+ readonly zip: F.Char;
734
+ readonly email: F.Char;
735
+ readonly phone: F.Char;
736
+ readonly website: F.Char;
709
737
  };
710
738
  readonly methods: ResCompanyMethods;
711
739
  }
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: e1b93f7adda98b39
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b268ad86cd210c36
2
2
  // Bindings of module `sale_account`: 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: e1b93f7adda98b39
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b268ad86cd210c36
2
2
  // Schema view of module `sale_account` (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
 
@@ -24,6 +24,7 @@ export interface SchemaView {
24
24
  readonly 'audit.event': AuditEvent;
25
25
  readonly 'auth.api_key': AuthApiKey;
26
26
  readonly 'auth.credential': AuthCredential;
27
+ readonly 'auth.policy': AuthPolicy;
27
28
  readonly 'auth.session': AuthSession;
28
29
  readonly 'auth.user_grant': AuthUserGrant;
29
30
  readonly 'auth.user_group': AuthUserGroup;
@@ -634,6 +635,26 @@ export interface AuthCredential {
634
635
  export interface AuthCredentialMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.credential'> {
635
636
  }
636
637
 
638
+ /** `auth.policy` (owner: base). */
639
+ export interface AuthPolicy {
640
+ readonly name: 'auth.policy';
641
+ readonly fields: {
642
+ readonly id: F.Id<'auth.policy'>;
643
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
644
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
645
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
646
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
647
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
648
+ readonly twoFactor: F.Selection<'administrators' | 'everyone', { readonly required: true; readonly default: true }>;
649
+ readonly sessionDays: F.Integer;
650
+ };
651
+ readonly methods: AuthPolicyMethods;
652
+ }
653
+
654
+ /** Methods of `auth.policy`: declared ones and transitions. */
655
+ export interface AuthPolicyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.policy'> {
656
+ }
657
+
637
658
  /** `auth.session` (owner: base). */
638
659
  export interface AuthSession {
639
660
  readonly name: 'auth.session';
@@ -1128,6 +1149,13 @@ export interface ResCompany {
1128
1149
  readonly name: F.Char<{ readonly required: true }>;
1129
1150
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
1130
1151
  readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
1152
+ readonly accentColor: F.Char;
1153
+ readonly street: F.Char;
1154
+ readonly city: F.Char;
1155
+ readonly zip: F.Char;
1156
+ readonly email: F.Char;
1157
+ readonly phone: F.Char;
1158
+ readonly website: F.Char;
1131
1159
  readonly fiscalYearStart: F.Integer<{ readonly required: true; readonly default: true }>; // accounting
1132
1160
  readonly chartTemplate: F.Char; // accounting
1133
1161
  readonly country: F.Char; // accounting
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 5a5c7561d67ef3cf
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2377e830cd00a651
2
2
  // Bindings of module `sale_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: 5a5c7561d67ef3cf
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2377e830cd00a651
2
2
  // Schema view of module `sale_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
 
@@ -8,6 +8,7 @@ export interface SchemaView {
8
8
  readonly 'audit.event': AuditEvent;
9
9
  readonly 'auth.api_key': AuthApiKey;
10
10
  readonly 'auth.credential': AuthCredential;
11
+ readonly 'auth.policy': AuthPolicy;
11
12
  readonly 'auth.session': AuthSession;
12
13
  readonly 'auth.user_grant': AuthUserGrant;
13
14
  readonly 'auth.user_group': AuthUserGroup;
@@ -139,6 +140,26 @@ export interface AuthCredential {
139
140
  export interface AuthCredentialMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.credential'> {
140
141
  }
141
142
 
143
+ /** `auth.policy` (owner: base). */
144
+ export interface AuthPolicy {
145
+ readonly name: 'auth.policy';
146
+ readonly fields: {
147
+ readonly id: F.Id<'auth.policy'>;
148
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
149
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
150
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
151
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
152
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
153
+ readonly twoFactor: F.Selection<'administrators' | 'everyone', { readonly required: true; readonly default: true }>;
154
+ readonly sessionDays: F.Integer;
155
+ };
156
+ readonly methods: AuthPolicyMethods;
157
+ }
158
+
159
+ /** Methods of `auth.policy`: declared ones and transitions. */
160
+ export interface AuthPolicyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.policy'> {
161
+ }
162
+
142
163
  /** `auth.session` (owner: base). */
143
164
  export interface AuthSession {
144
165
  readonly name: 'auth.session';
@@ -552,6 +573,13 @@ export interface ResCompany {
552
573
  readonly name: F.Char<{ readonly required: true }>;
553
574
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
554
575
  readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
576
+ readonly accentColor: F.Char;
577
+ readonly street: F.Char;
578
+ readonly city: F.Char;
579
+ readonly zip: F.Char;
580
+ readonly email: F.Char;
581
+ readonly phone: F.Char;
582
+ readonly website: F.Char;
555
583
  };
556
584
  readonly methods: ResCompanyMethods;
557
585
  }
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 698d6a45d2c40df8
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b044bca835096ac0
2
2
  // Bindings of module `sale_project`: 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: 698d6a45d2c40df8
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b044bca835096ac0
2
2
  // Schema view of module `sale_project` (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
 
@@ -8,6 +8,7 @@ export interface SchemaView {
8
8
  readonly 'audit.event': AuditEvent;
9
9
  readonly 'auth.api_key': AuthApiKey;
10
10
  readonly 'auth.credential': AuthCredential;
11
+ readonly 'auth.policy': AuthPolicy;
11
12
  readonly 'auth.session': AuthSession;
12
13
  readonly 'auth.user_grant': AuthUserGrant;
13
14
  readonly 'auth.user_group': AuthUserGroup;
@@ -140,6 +141,26 @@ export interface AuthCredential {
140
141
  export interface AuthCredentialMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.credential'> {
141
142
  }
142
143
 
144
+ /** `auth.policy` (owner: base). */
145
+ export interface AuthPolicy {
146
+ readonly name: 'auth.policy';
147
+ readonly fields: {
148
+ readonly id: F.Id<'auth.policy'>;
149
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
150
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
151
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
152
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
153
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
154
+ readonly twoFactor: F.Selection<'administrators' | 'everyone', { readonly required: true; readonly default: true }>;
155
+ readonly sessionDays: F.Integer;
156
+ };
157
+ readonly methods: AuthPolicyMethods;
158
+ }
159
+
160
+ /** Methods of `auth.policy`: declared ones and transitions. */
161
+ export interface AuthPolicyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.policy'> {
162
+ }
163
+
143
164
  /** `auth.session` (owner: base). */
144
165
  export interface AuthSession {
145
166
  readonly name: 'auth.session';
@@ -596,6 +617,13 @@ export interface ResCompany {
596
617
  readonly name: F.Char<{ readonly required: true }>;
597
618
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
598
619
  readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
620
+ readonly accentColor: F.Char;
621
+ readonly street: F.Char;
622
+ readonly city: F.Char;
623
+ readonly zip: F.Char;
624
+ readonly email: F.Char;
625
+ readonly phone: F.Char;
626
+ readonly website: F.Char;
599
627
  };
600
628
  readonly methods: ResCompanyMethods;
601
629
  }
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: b513566045fc25a0
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2d9d91e59673d178
2
2
  // Bindings of module `sales`: 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: b513566045fc25a0
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2d9d91e59673d178
2
2
  // Schema view of module `sales` (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
 
@@ -8,6 +8,7 @@ export interface SchemaView {
8
8
  readonly 'audit.event': AuditEvent;
9
9
  readonly 'auth.api_key': AuthApiKey;
10
10
  readonly 'auth.credential': AuthCredential;
11
+ readonly 'auth.policy': AuthPolicy;
11
12
  readonly 'auth.session': AuthSession;
12
13
  readonly 'auth.user_grant': AuthUserGrant;
13
14
  readonly 'auth.user_group': AuthUserGroup;
@@ -136,6 +137,26 @@ export interface AuthCredential {
136
137
  export interface AuthCredentialMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.credential'> {
137
138
  }
138
139
 
140
+ /** `auth.policy` (owner: base). */
141
+ export interface AuthPolicy {
142
+ readonly name: 'auth.policy';
143
+ readonly fields: {
144
+ readonly id: F.Id<'auth.policy'>;
145
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
146
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
147
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
148
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
149
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
150
+ readonly twoFactor: F.Selection<'administrators' | 'everyone', { readonly required: true; readonly default: true }>;
151
+ readonly sessionDays: F.Integer;
152
+ };
153
+ readonly methods: AuthPolicyMethods;
154
+ }
155
+
156
+ /** Methods of `auth.policy`: declared ones and transitions. */
157
+ export interface AuthPolicyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'auth.policy'> {
158
+ }
159
+
139
160
  /** `auth.session` (owner: base). */
140
161
  export interface AuthSession {
141
162
  readonly name: 'auth.session';
@@ -477,6 +498,13 @@ export interface ResCompany {
477
498
  readonly name: F.Char<{ readonly required: true }>;
478
499
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
479
500
  readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
501
+ readonly accentColor: F.Char;
502
+ readonly street: F.Char;
503
+ readonly city: F.Char;
504
+ readonly zip: F.Char;
505
+ readonly email: F.Char;
506
+ readonly phone: F.Char;
507
+ readonly website: F.Char;
480
508
  };
481
509
  readonly methods: ResCompanyMethods;
482
510
  }
@@ -17,6 +17,8 @@ export const sales = defineModule({
17
17
  kosh: '^0.1.0',
18
18
  hooks: { demo: salesDemo },
19
19
  depends: [contacts, product, messaging],
20
+ // What webhooks may subscribe to besides record changes (RFC 0017 §5.3).
21
+ events: [{ topic: 'sale.order.confirmed', model: 'sale.order', label: 'Sales order confirmed' }],
20
22
  app: {
21
23
  code: 'sales',
22
24
  label: 'Sales',
@@ -17,8 +17,13 @@
17
17
  * - `GET /api/v1/openapi.json` the OpenAPI 3.1 document
18
18
  */
19
19
  import type { FastifyInstance } from 'fastify';
20
- import type { Driver } from '../orm.js';
20
+ import type { Driver, Env } from '../orm.js';
21
21
  import { type ApiModel } from './metadata.js';
22
+ /**
23
+ * A record as `GET /api/v1/models/<model>/<id>` returns it to `env`'s user (their readable default fields), or null
24
+ * when they cannot see it: what webhooks send (RFC 0017 §5.3).
25
+ */
26
+ export declare function readApiRecord(env: Env, model: ApiModel, id: number): Promise<Record<string, unknown> | null>;
22
27
  /** Registers the generated routes for `models`. */
23
28
  export declare function registerRestApi(app: FastifyInstance, options: {
24
29
  readonly models: ReadonlyMap<string, ApiModel>;
@@ -102,6 +102,17 @@ function ifMatch(request) {
102
102
  throw problem(400, 'invalid-if-match', 'Invalid If-Match', 'If-Match must be an ETag returned by this API.');
103
103
  return new Date(Number(m[1]));
104
104
  }
105
+ /**
106
+ * A record as `GET /api/v1/models/<model>/<id>` returns it to `env`'s user (their readable default fields), or null
107
+ * when they cannot see it: what webhooks send (RFC 0017 §5.3).
108
+ */
109
+ export async function readApiRecord(env, model, id) {
110
+ const set = await ref(env, model.name).search({ where: { id }, limit: 1 });
111
+ if (set.length === 0)
112
+ return null;
113
+ const [row] = await rows(set, fieldsOf(model, env, undefined));
114
+ return row ?? null;
115
+ }
105
116
  async function record(env, model, id, fields) {
106
117
  const set = await ref(env, model.name).browse([id]).load(['id', 'updatedAt']);
107
118
  const [row] = await rows(set, fields ?? fieldsOf(model, env, undefined));
@@ -22,6 +22,15 @@ const ResCompany = defineModel('res.company', {
22
22
  currency: f.many2one('res.currency', { required: true }),
23
23
  /** How numbers are grouped on screen and in documents (RFC 0016): 1,234,567 or 12,34,567 (lakh and crore). */
24
24
  numberGrouping: f.selection({ international: 'International (1,234,567)', lakh: 'Lakh and crore (12,34,567)' }, { required: true, default: 'international', label: 'Number grouping' }),
25
+ /** The company's brand colour (`#4f46e5`), the back-office's accent (RFC 0018); the default when empty. */
26
+ accentColor: f.char({ size: 7, label: 'Brand colour' }),
27
+ /** Its address and how to reach it (RFC 0019 §3.4): on documents, e-invoices and the portal. */
28
+ street: f.char({ label: 'Address' }),
29
+ city: f.char({ label: 'City' }),
30
+ zip: f.char({ size: 12, label: 'Postal code' }),
31
+ email: f.char({ label: 'Email' }),
32
+ phone: f.char({ label: 'Phone' }),
33
+ website: f.char({ label: 'Website' }),
25
34
  },
26
35
  });
27
36
  const readRows = async (env, model, ids, keys) => env.asSuperuser({ reason: 'check administrators' }, async (su) => {
@@ -127,6 +136,17 @@ const AuthUserGrant = defineModel('auth.user_grant', {
127
136
  reason: f.char({ required: true }),
128
137
  },
129
138
  });
139
+ /**
140
+ * The installation's security policy (RFC 0019 §3.5): one row, read by the auth service, changed in Settings ›
141
+ * Security. Without a row: two-factor sign-in for administrators only, sessions as the server is configured.
142
+ */
143
+ const AuthPolicy = defineModel('auth.policy', {
144
+ fields: {
145
+ twoFactor: f.selection({ administrators: 'Administrators', everyone: 'Everyone' }, { required: true, default: 'administrators', label: 'Two-factor sign-in required for' }),
146
+ /** Days a sign-in lasts at most; empty: the server's setting. */
147
+ sessionDays: f.integer({ label: 'Session lifetime (days)' }),
148
+ },
149
+ });
130
150
  /**
131
151
  * The security audit log (CLAUDE.md: logins, permission changes, exports, sensitive actions). Append-only: no one
132
152
  * may change or delete events (administrators included: the REST API never serves it, and the admin API only reads).
@@ -196,6 +216,7 @@ export const baseShutro = defineModule({
196
216
  AuthApiKey,
197
217
  AuthUserGroup,
198
218
  AuthUserGrant,
219
+ AuthPolicy,
199
220
  AuditEvent,
200
221
  SavedView,
201
222
  ],
@@ -214,6 +235,7 @@ export const base = defineKoshModule({
214
235
  'auth.api_key',
215
236
  'auth.user_group',
216
237
  'auth.user_grant',
238
+ 'auth.policy',
217
239
  'audit.event',
218
240
  ],
219
241
  });
@@ -10,6 +10,9 @@
10
10
  * - `POST /api/v1/auth/2fa/verify` `{ code }` (TOTP or recovery code) completes a pending sign-in
11
11
  * - `POST /api/v1/auth/2fa/disable` `{ password, code }`
12
12
  * - `POST /api/v1/auth/password` `{ current, next }`; other sessions are revoked
13
+ * - `GET /api/v1/auth/profile` your details (RFC 0019); `PATCH` `{ name?, email?, phone?, lang?, tz?, company? }`
14
+ * - `GET /api/v1/auth/sessions` your signed-in devices; `DELETE …/sessions/:id` signs one out
15
+ * - `POST /api/v1/auth/sessions/sign-out-others`
13
16
  */
14
17
  import type { FastifyInstance } from 'fastify';
15
18
  import type { AuthService } from './service.js';
@@ -52,4 +52,33 @@ export function registerAuthRoutes(app, auth, options = {}) {
52
52
  await auth.changePassword(await auth.requireSession(request), text(request, 'current'), text(request, 'next'));
53
53
  return { ok: true };
54
54
  });
55
+ // Your own profile and sessions (RFC 0019 §3.6): a session's, never an API key's.
56
+ app.get('/api/v1/auth/profile', { config: { auth: false } }, async (request) => auth.profile(await auth.requireSession(request)));
57
+ app.patch('/api/v1/auth/profile', { config: { auth: false } }, async (request) => {
58
+ const body = (request.body ?? {});
59
+ const values = {};
60
+ for (const key of ['name', 'email', 'phone', 'lang', 'tz'])
61
+ if (body[key] !== undefined) {
62
+ if (body[key] !== null &&
63
+ (typeof body[key] !== 'string' || body[key].length > 200))
64
+ throw new HttpProblem(400, 'invalid-body', 'Invalid body', `'${key}' is a short text.`);
65
+ values[key] = body[key];
66
+ }
67
+ if (body['company'] !== undefined) {
68
+ if (!Number.isSafeInteger(body['company']))
69
+ throw new HttpProblem(400, 'invalid-body', 'Invalid body', "'company' is a company id.");
70
+ values['company'] = body['company'];
71
+ }
72
+ return auth.updateProfile(await auth.requireSession(request), values);
73
+ });
74
+ app.get('/api/v1/auth/sessions', { config: { auth: false } }, async (request) => ({
75
+ sessions: await auth.sessions(await auth.requireSession(request)),
76
+ }));
77
+ app.delete('/api/v1/auth/sessions/:id', { config: { auth: false } }, async (request) => {
78
+ const id = Number(request.params.id);
79
+ if (!Number.isSafeInteger(id) || id <= 0)
80
+ throw new HttpProblem(400, 'invalid-id', 'Invalid id', 'The id is a positive integer.');
81
+ return auth.revokeSessions(await auth.requireSession(request), id);
82
+ });
83
+ app.post('/api/v1/auth/sessions/sign-out-others', { config: { auth: false } }, async (request) => auth.revokeSessions(await auth.requireSession(request), 'others'));
55
84
  }
@@ -39,11 +39,22 @@ export interface UserInfo {
39
39
  readonly companies: readonly number[];
40
40
  /** The number grouping of each of the user's companies (RFC 0016): `lakh` or `international`. */
41
41
  readonly groupings: Readonly<Record<string, 'international' | 'lakh'>>;
42
+ /** The brand colour of each of the user's companies that has one (RFC 0018): `#rrggbb`. */
43
+ readonly accents: Readonly<Record<string, string>>;
42
44
  readonly twoFactorEnabled: boolean;
43
45
  /** Holds an administrator role (in some company). */
44
46
  readonly administrator: boolean;
45
47
  /** Administrator rights are withheld until this session has a second factor (enable or verify 2FA). */
46
48
  readonly administratorNeedsSecondFactor: boolean;
49
+ /** The policy requires two-factor sign-in of everyone and it is not on: nothing but setting it up is allowed. */
50
+ readonly twoFactorSetupRequired: boolean;
51
+ }
52
+ /** The installation's security policy (RFC 0019 §3.5). */
53
+ export interface SecurityPolicy {
54
+ /** Who must sign in with a second factor: administrators only (the default), or everyone. */
55
+ readonly twoFactor: 'administrators' | 'everyone';
56
+ /** Days a sign-in lasts at most; null: the server's `lifetimeMs`. */
57
+ readonly sessionDays: number | null;
47
58
  }
48
59
  /** An API key as listed (never its secret). */
49
60
  export interface ApiKeyInfo {
@@ -162,4 +173,45 @@ export declare class AuthService {
162
173
  disableTwoFactor(session: SessionInfo, password: string, code: string): Promise<void>;
163
174
  /** Changes the password; every other session of the user is revoked. */
164
175
  changePassword(session: SessionInfo, current: string, next: string): Promise<void>;
176
+ /** The security policy (cached like sessions; {@link forgetPolicy} after changing it). */
177
+ policy(): Promise<SecurityPolicy>;
178
+ /** Forgets the cached policy: after Settings › Security changed it. */
179
+ forgetPolicy(): void;
180
+ /** Forgets what is cached about `user` (their identity and sessions): after their roles or account changed. */
181
+ forget(user: number): void;
182
+ /** The signed-in user's own details (RFC 0019 §3.6). */
183
+ profile(session: SessionInfo): Promise<Profile>;
184
+ /**
185
+ * Changes the user's own details: name, email, phone, language, time zone, and the default company (one of
186
+ * theirs). Audited.
187
+ */
188
+ updateProfile(session: SessionInfo, values: Partial<Omit<Profile, 'login'>>): Promise<Profile>;
189
+ /** The user's active sessions, newest use first; the current one marked. */
190
+ sessions(session: SessionInfo): Promise<SessionEntry[]>;
191
+ /** Signs out one of the user's own sessions (or all but this one with `'others'`). Audited. */
192
+ revokeSessions(session: SessionInfo, which: number | 'others'): Promise<{
193
+ revoked: number;
194
+ }>;
195
+ }
196
+ /** The user's own details (RFC 0019 §3.6). */
197
+ export interface Profile {
198
+ readonly login: string;
199
+ readonly name: string;
200
+ readonly email: string | null;
201
+ readonly phone: string | null;
202
+ readonly lang: string;
203
+ readonly tz: string;
204
+ /** The default company. */
205
+ readonly company: number | null;
206
+ }
207
+ /** One of the user's sessions (a signed-in device). */
208
+ export interface SessionEntry {
209
+ readonly id: number;
210
+ readonly ip: string | null;
211
+ readonly userAgent: string | null;
212
+ readonly lastSeenAt: string;
213
+ readonly createdAt: string | null;
214
+ readonly secondFactor: boolean;
215
+ /** This browser. */
216
+ readonly current: boolean;
165
217
  }