shutro-erp 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +17 -10
  2. package/dist/account_portal/.kosh/bindings.d.ts +22 -0
  3. package/dist/account_portal/.kosh/bindings.js +15 -0
  4. package/dist/account_portal/.kosh/schema.d.ts +1350 -0
  5. package/dist/account_portal/index.d.ts +3 -0
  6. package/dist/account_portal/index.js +73 -0
  7. package/dist/account_portal/module.d.ts +5983 -0
  8. package/dist/account_portal/module.js +18 -0
  9. package/dist/accounting/.kosh/bindings.js +1 -1
  10. package/dist/accounting/.kosh/schema.d.ts +142 -9
  11. package/dist/accounting/chart.d.ts +40 -8
  12. package/dist/accounting/chart.js +124 -30
  13. package/dist/accounting/closing.d.ts +6 -1
  14. package/dist/accounting/closing.js +5 -1
  15. package/dist/accounting/demo.js +6 -5
  16. package/dist/accounting/index.d.ts +3 -3
  17. package/dist/accounting/index.js +200 -23
  18. package/dist/accounting/module.d.ts +3326 -1086
  19. package/dist/accounting/module.js +408 -66
  20. package/dist/accounting/payments.d.ts +25 -0
  21. package/dist/accounting/payments.js +96 -4
  22. package/dist/accounting/web/RegisterPayment.d.ts +3 -2
  23. package/dist/accounting/web/RegisterPayment.js +39 -6
  24. package/dist/accounting/web/SettingsPage.js +5 -1
  25. package/dist/accounting/web/messages.js +10 -0
  26. package/dist/cli/init.js +172 -48
  27. package/dist/cli/launch.d.ts +16 -0
  28. package/dist/cli/launch.js +138 -0
  29. package/dist/cli/prompt.d.ts +35 -0
  30. package/dist/cli/prompt.js +46 -0
  31. package/dist/cli/templates.d.ts +11 -3
  32. package/dist/cli/templates.js +32 -7
  33. package/dist/cli/tui.d.ts +7 -0
  34. package/dist/cli/tui.js +254 -0
  35. package/dist/cli/ui.d.ts +25 -5
  36. package/dist/cli/ui.js +49 -9
  37. package/dist/contacts/.kosh/bindings.js +1 -1
  38. package/dist/contacts/.kosh/schema.d.ts +3 -1
  39. package/dist/contacts/index.js +24 -1
  40. package/dist/contacts/module.d.ts +42 -0
  41. package/dist/contacts/module.js +3 -1
  42. package/dist/import/convert.d.ts +55 -0
  43. package/dist/import/convert.js +224 -0
  44. package/dist/import/csv.d.ts +28 -0
  45. package/dist/import/csv.js +115 -0
  46. package/dist/import/index.d.ts +33 -0
  47. package/dist/import/index.js +60 -0
  48. package/dist/import/mapping.d.ts +15 -0
  49. package/dist/import/mapping.js +33 -0
  50. package/dist/import/match.d.ts +24 -0
  51. package/dist/import/match.js +60 -0
  52. package/dist/import/models.d.ts +2 -0
  53. package/dist/import/models.js +102 -0
  54. package/dist/import/routes.d.ts +13 -0
  55. package/dist/import/routes.js +392 -0
  56. package/dist/import/run.d.ts +131 -0
  57. package/dist/import/run.js +568 -0
  58. package/dist/import/service.d.ts +153 -0
  59. package/dist/import/service.js +367 -0
  60. package/dist/import/web/ImportButton.d.ts +7 -0
  61. package/dist/import/web/ImportButton.js +31 -0
  62. package/dist/import/web/ImportWizard.d.ts +75 -0
  63. package/dist/import/web/ImportWizard.js +217 -0
  64. package/dist/import/web/index.d.ts +13 -0
  65. package/dist/import/web/index.js +18 -0
  66. package/dist/import/web/messages.d.ts +2 -0
  67. package/dist/import/web/messages.js +139 -0
  68. package/dist/import/worker.d.ts +3 -0
  69. package/dist/import/worker.js +52 -0
  70. package/dist/import/xlsx.d.ts +20 -0
  71. package/dist/import/xlsx.js +192 -0
  72. package/dist/l10n_bd/.kosh/bindings.d.ts +22 -0
  73. package/dist/l10n_bd/.kosh/bindings.js +15 -0
  74. package/dist/l10n_bd/.kosh/schema.d.ts +1281 -0
  75. package/dist/l10n_bd/documents.d.ts +8 -0
  76. package/dist/l10n_bd/documents.js +183 -0
  77. package/dist/l10n_bd/index.d.ts +8 -0
  78. package/dist/l10n_bd/index.js +156 -0
  79. package/dist/l10n_bd/module.d.ts +5705 -0
  80. package/dist/l10n_bd/module.js +163 -0
  81. package/dist/l10n_bd/mushak.d.ts +67 -0
  82. package/dist/l10n_bd/mushak.js +139 -0
  83. package/dist/l10n_bd/routes.d.ts +3 -0
  84. package/dist/l10n_bd/routes.js +205 -0
  85. package/dist/l10n_bd/web/MushakPage.d.ts +7 -0
  86. package/dist/l10n_bd/web/MushakPage.js +53 -0
  87. package/dist/l10n_bd/web/index.d.ts +13 -0
  88. package/dist/l10n_bd/web/index.js +18 -0
  89. package/dist/l10n_bd/web/messages.d.ts +2 -0
  90. package/dist/l10n_bd/web/messages.js +101 -0
  91. package/dist/l10n_in/.kosh/bindings.d.ts +22 -0
  92. package/dist/l10n_in/.kosh/bindings.js +15 -0
  93. package/dist/l10n_in/.kosh/schema.d.ts +1343 -0
  94. package/dist/l10n_in/documents.d.ts +8 -0
  95. package/dist/l10n_in/documents.js +114 -0
  96. package/dist/l10n_in/einvoice.d.ts +253 -0
  97. package/dist/l10n_in/einvoice.js +485 -0
  98. package/dist/l10n_in/gstr.d.ts +109 -0
  99. package/dist/l10n_in/gstr.js +430 -0
  100. package/dist/l10n_in/index.d.ts +20 -0
  101. package/dist/l10n_in/index.js +232 -0
  102. package/dist/l10n_in/module.d.ts +7823 -0
  103. package/dist/l10n_in/module.js +427 -0
  104. package/dist/l10n_in/routes-einvoice.d.ts +15 -0
  105. package/dist/l10n_in/routes-einvoice.js +124 -0
  106. package/dist/l10n_in/routes.d.ts +17 -0
  107. package/dist/l10n_in/routes.js +282 -0
  108. package/dist/l10n_in/web/EInvoicePanel.d.ts +8 -0
  109. package/dist/l10n_in/web/EInvoicePanel.js +63 -0
  110. package/dist/l10n_in/web/GstPage.d.ts +7 -0
  111. package/dist/l10n_in/web/GstPage.js +57 -0
  112. package/dist/l10n_in/web/GstSettings.d.ts +10 -0
  113. package/dist/l10n_in/web/GstSettings.js +36 -0
  114. package/dist/l10n_in/web/index.d.ts +15 -0
  115. package/dist/l10n_in/web/index.js +22 -0
  116. package/dist/l10n_in/web/messages.d.ts +2 -0
  117. package/dist/l10n_in/web/messages.js +166 -0
  118. package/dist/modules-sdk/demo.d.ts +2 -0
  119. package/dist/modules-sdk/demo.js +7 -3
  120. package/dist/modules-sdk/index.d.ts +2 -2
  121. package/dist/modules-sdk/index.js +1 -1
  122. package/dist/modules-sdk/manifest.d.ts +132 -0
  123. package/dist/modules-sdk/manifest.js +22 -0
  124. package/dist/notify/inbox.js +21 -0
  125. package/dist/notify/messages.js +4 -0
  126. package/dist/notify/models.d.ts +1 -1
  127. package/dist/notify/models.js +2 -0
  128. package/dist/notify/web/messages.js +4 -0
  129. package/dist/portal/.kosh/bindings.d.ts +22 -0
  130. package/dist/portal/.kosh/bindings.js +15 -0
  131. package/dist/portal/.kosh/schema.d.ts +346 -0
  132. package/dist/portal/announce.d.ts +16 -0
  133. package/dist/portal/announce.js +117 -0
  134. package/dist/portal/documents.d.ts +17 -0
  135. package/dist/portal/documents.js +61 -0
  136. package/dist/portal/index.d.ts +20 -0
  137. package/dist/portal/index.js +65 -0
  138. package/dist/portal/module.d.ts +1405 -0
  139. package/dist/portal/module.js +84 -0
  140. package/dist/portal/routes-documents.d.ts +2 -0
  141. package/dist/portal/routes-documents.js +391 -0
  142. package/dist/portal/routes.d.ts +13 -0
  143. package/dist/portal/routes.js +313 -0
  144. package/dist/portal/rules.d.ts +11 -0
  145. package/dist/portal/rules.js +25 -0
  146. package/dist/portal/web/PortalAccess.d.ts +8 -0
  147. package/dist/portal/web/PortalAccess.js +66 -0
  148. package/dist/portal/web/index.d.ts +12 -0
  149. package/dist/portal/web/index.js +17 -0
  150. package/dist/portal/web/messages.d.ts +2 -0
  151. package/dist/portal/web/messages.js +33 -0
  152. package/dist/product/.kosh/bindings.js +1 -1
  153. package/dist/product/.kosh/schema.d.ts +12 -3
  154. package/dist/product/demo.js +6 -5
  155. package/dist/product/index.d.ts +1 -0
  156. package/dist/product/index.js +17 -4
  157. package/dist/product/module.d.ts +346 -14
  158. package/dist/product/module.js +48 -18
  159. package/dist/product/taxes.d.ts +59 -0
  160. package/dist/product/taxes.js +61 -0
  161. package/dist/project/.kosh/bindings.js +1 -1
  162. package/dist/project/.kosh/schema.d.ts +3 -1
  163. package/dist/project/module.d.ts +20 -0
  164. package/dist/project_portal/.kosh/bindings.d.ts +22 -0
  165. package/dist/project_portal/.kosh/bindings.js +15 -0
  166. package/dist/project_portal/.kosh/schema.d.ts +671 -0
  167. package/dist/project_portal/index.d.ts +9 -0
  168. package/dist/project_portal/index.js +42 -0
  169. package/dist/project_portal/module.d.ts +2142 -0
  170. package/dist/project_portal/module.js +22 -0
  171. package/dist/purchase/.kosh/bindings.js +1 -1
  172. package/dist/purchase/.kosh/schema.d.ts +15 -4
  173. package/dist/purchase/demo.js +3 -3
  174. package/dist/purchase/index.js +1 -1
  175. package/dist/purchase/module.d.ts +232 -8
  176. package/dist/purchase/module.js +51 -9
  177. package/dist/purchase_account/.kosh/bindings.js +1 -1
  178. package/dist/purchase_account/.kosh/schema.d.ts +144 -10
  179. package/dist/purchase_account/module.d.ts +2008 -641
  180. package/dist/purchase_account/module.js +2 -2
  181. package/dist/purchase_portal/.kosh/bindings.d.ts +22 -0
  182. package/dist/purchase_portal/.kosh/bindings.js +15 -0
  183. package/dist/purchase_portal/.kosh/schema.d.ts +822 -0
  184. package/dist/purchase_portal/index.d.ts +9 -0
  185. package/dist/purchase_portal/index.js +53 -0
  186. package/dist/purchase_portal/module.d.ts +2758 -0
  187. package/dist/purchase_portal/module.js +18 -0
  188. package/dist/reporting/documents.js +20 -3
  189. package/dist/reporting/index.d.ts +1 -0
  190. package/dist/reporting/index.js +1 -0
  191. package/dist/reporting/routes-documents.js +9 -2
  192. package/dist/reporting/words.d.ts +11 -0
  193. package/dist/reporting/words.js +229 -0
  194. package/dist/sale_account/.kosh/bindings.js +1 -1
  195. package/dist/sale_account/.kosh/schema.d.ts +144 -10
  196. package/dist/sale_account/module.d.ts +2008 -641
  197. package/dist/sale_account/module.js +3 -3
  198. package/dist/sale_portal/.kosh/bindings.d.ts +22 -0
  199. package/dist/sale_portal/.kosh/bindings.js +15 -0
  200. package/dist/sale_portal/.kosh/schema.d.ts +736 -0
  201. package/dist/sale_portal/index.d.ts +9 -0
  202. package/dist/sale_portal/index.js +71 -0
  203. package/dist/sale_portal/module.d.ts +2718 -0
  204. package/dist/sale_portal/module.js +19 -0
  205. package/dist/sale_project/.kosh/bindings.js +1 -1
  206. package/dist/sale_project/.kosh/schema.d.ts +15 -4
  207. package/dist/sale_project/module.d.ts +390 -10
  208. package/dist/sales/.kosh/bindings.js +1 -1
  209. package/dist/sales/.kosh/schema.d.ts +15 -4
  210. package/dist/sales/demo.js +3 -3
  211. package/dist/sales/index.js +67 -1
  212. package/dist/sales/module.d.ts +232 -8
  213. package/dist/sales/module.js +51 -12
  214. package/dist/server/auth/base.js +2 -0
  215. package/dist/server/auth/service.d.ts +2 -0
  216. package/dist/server/auth/service.js +7 -1
  217. package/dist/server/index.d.ts +1 -1
  218. package/dist/server/index.js +1 -1
  219. package/dist/server/server.js +26 -1
  220. package/dist/ui/api.d.ts +2 -0
  221. package/dist/ui/app.js +1 -1
  222. package/dist/ui/context.d.ts +3 -1
  223. package/dist/ui/context.js +2 -2
  224. package/dist/ui/index.d.ts +1 -1
  225. package/dist/ui/index.js +1 -1
  226. package/dist/ui/lib/format.d.ts +2 -0
  227. package/dist/ui/lib/format.js +3 -1
  228. package/dist/ui/views/FormView.js +19 -6
  229. package/package.json +50 -2
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 2d429d925bd4e737
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 531f9724f4925363
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
 
@@ -43,7 +43,13 @@ export interface AccountTax {
43
43
  readonly active: F.Bool<{ readonly required: true; readonly default: true }>;
44
44
  readonly name: F.Char<{ readonly required: true; readonly translate: true }>;
45
45
  readonly use: F.Selection<'sale' | 'purchase', { readonly required: true; readonly default: true }>;
46
+ readonly sequence: F.Integer<{ readonly required: true; readonly default: true }>;
47
+ readonly amountType: F.Selection<'percent' | 'fixed', { readonly required: true; readonly default: true }>;
46
48
  readonly rate: F.Decimal<{ readonly required: true; readonly default: true }>;
49
+ readonly fixedAmount: F.Decimal<{ readonly required: true; readonly default: true }>;
50
+ readonly affectsBase: F.Bool<{ readonly required: true; readonly default: true }>;
51
+ readonly priceIncluded: F.Bool<{ readonly required: true; readonly default: true }>;
52
+ readonly reverseCharge: F.Bool<{ readonly required: true; readonly default: true }>;
47
53
  };
48
54
  readonly methods: AccountTaxMethods;
49
55
  }
@@ -416,8 +422,10 @@ export interface ProductProduct {
416
422
  readonly standardPrice: F.Decimal<{ readonly required: true; readonly default: true }>;
417
423
  readonly saleOk: F.Bool<{ readonly required: true; readonly default: true }>;
418
424
  readonly purchaseOk: F.Bool<{ readonly required: true; readonly default: true }>;
419
- readonly tax: F.Many2one<'account.tax'>;
420
- readonly purchaseTax: F.Many2one<'account.tax'>;
425
+ readonly taxes: F.Many2many<'account.tax', { readonly required: true }>;
426
+ readonly purchaseTaxes: F.Many2many<'account.tax', { readonly required: true }>;
427
+ readonly tax: F.Many2one<'account.tax', { readonly readonly: true }>;
428
+ readonly purchaseTax: F.Many2one<'account.tax', { readonly readonly: true }>;
421
429
  readonly description: F.Text<{ readonly translate: true }>;
422
430
  };
423
431
  readonly methods: ProductProductMethods;
@@ -439,6 +447,7 @@ export interface ResCompany {
439
447
  readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
440
448
  readonly name: F.Char<{ readonly required: true }>;
441
449
  readonly currency: F.Many2one<'res.currency', { readonly required: true }>;
450
+ readonly numberGrouping: F.Selection<'international' | 'lakh', { readonly required: true; readonly default: true }>;
442
451
  };
443
452
  readonly methods: ResCompanyMethods;
444
453
  }
@@ -492,6 +501,7 @@ export interface ResPartner {
492
501
  readonly city: F.Char;
493
502
  readonly zip: F.Char;
494
503
  readonly country: F.Char;
504
+ readonly state: F.Char;
495
505
  readonly taxId: F.Char;
496
506
  readonly isCustomer: F.Bool<{ readonly required: true; readonly default: true }>;
497
507
  readonly isVendor: F.Bool<{ readonly required: true; readonly default: true }>;
@@ -587,7 +597,8 @@ export interface SaleOrderLine {
587
597
  readonly uom: F.Many2one<'uom.uom'>;
588
598
  readonly priceUnit: F.Decimal<{ readonly required: true; readonly default: true }>;
589
599
  readonly discount: F.Decimal<{ readonly required: true; readonly default: true }>;
590
- readonly tax: F.Many2one<'account.tax'>;
600
+ readonly taxes: F.Many2many<'account.tax', { readonly required: true }>;
601
+ readonly tax: F.Many2one<'account.tax', { readonly readonly: true }>;
591
602
  readonly currency: F.Many2one<'res.currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
592
603
  readonly subtotal: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
593
604
  readonly taxAmount: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
@@ -1,5 +1,5 @@
1
1
  /** Demo data of Sales (RFC 0008 §5.8): quotations and confirmed orders of a salesperson, with VAT. */
2
- import { idByName } from '../modules-sdk/index.js';
2
+ import { idByName, maybeIdByName } from '../modules-sdk/index.js';
3
3
  import { orm } from '../server/orm.js';
4
4
  /** The demo salesperson. */
5
5
  export const RAHIM = { login: 'rahim', name: 'Rahim Uddin', groups: ['sales.user'] };
@@ -54,7 +54,7 @@ export async function salesDemo(context) {
54
54
  .load({ currency: true });
55
55
  if (company === undefined)
56
56
  throw new Error('Demo data: no company.');
57
- const vat = await idByName(env, 'account.tax', 'VAT 15%');
57
+ const vat = await maybeIdByName(env, 'account.tax', 'VAT 15%');
58
58
  let confirmed = 0;
59
59
  for (const o of ORDERS) {
60
60
  const lines = [];
@@ -65,7 +65,7 @@ export async function salesDemo(context) {
65
65
  name: l.product,
66
66
  quantity: l.quantity,
67
67
  priceUnit: l.price,
68
- tax: orm.asId(vat, 'account.tax'),
68
+ taxes: { set: vat === null ? [] : [orm.asId(vat, 'account.tax')] },
69
69
  });
70
70
  }
71
71
  const created = await env.model('sale.order').create([
@@ -29,6 +29,72 @@ export const sales = defineModule({
29
29
  user: { label: 'Salesperson (own orders)', groups: ['sales.user'] },
30
30
  manager: { label: 'Manager (all orders)', groups: ['sales.manager'], implies: ['user'] },
31
31
  },
32
+ // The printed quotation (RFC 0005), installed as a template designers can change; the portal prints it too.
33
+ documents: [
34
+ {
35
+ key: 'quotation',
36
+ name: 'Quotation',
37
+ model: 'sale.order',
38
+ layout: {
39
+ paper: 'A4',
40
+ header: '{{company.name}}',
41
+ footer: 'Page {{page}} of {{pages}}',
42
+ blocks: [
43
+ { kind: 'heading', id: 'title', text: '{{state}} {{name}}', level: 1 },
44
+ {
45
+ kind: 'columns',
46
+ id: 'parties',
47
+ left: [
48
+ {
49
+ kind: 'fields',
50
+ id: 'customer',
51
+ items: [
52
+ { label: 'Customer', bind: 'partner.name' },
53
+ { label: 'Customer reference', bind: 'clientReference' },
54
+ ],
55
+ },
56
+ ],
57
+ right: [
58
+ {
59
+ kind: 'fields',
60
+ id: 'dates',
61
+ items: [
62
+ { label: 'Order date', bind: 'dateOrder' },
63
+ { label: 'Valid until', bind: 'validityDate' },
64
+ { label: 'Salesperson', bind: 'user.name' },
65
+ ],
66
+ },
67
+ ],
68
+ },
69
+ {
70
+ kind: 'table',
71
+ id: 'lines',
72
+ bind: 'lines',
73
+ columns: [
74
+ { label: 'Description', bind: 'name' },
75
+ { label: 'Quantity', bind: 'quantity', align: 'end' },
76
+ { label: 'Unit price', bind: 'priceUnit', align: 'end' },
77
+ { label: 'Discount %', bind: 'discount', align: 'end' },
78
+ { label: 'Tax', bind: 'taxAmount', align: 'end' },
79
+ { label: 'Subtotal', bind: 'subtotal', align: 'end', sum: true },
80
+ ],
81
+ },
82
+ {
83
+ kind: 'fields',
84
+ id: 'totals',
85
+ columns: 3,
86
+ items: [
87
+ { label: 'Untaxed', bind: 'amountUntaxed' },
88
+ { label: 'Taxes', bind: 'amountTax' },
89
+ { label: 'Total', bind: 'amountTotal' },
90
+ ],
91
+ },
92
+ { kind: 'text', id: 'in_words', text: '{{amountTotal|words}}' },
93
+ { kind: 'text', id: 'terms', text: '{{note}}' },
94
+ ],
95
+ },
96
+ },
97
+ ],
32
98
  actions: {
33
99
  'sales.action_quotations': action(SaleOrder, {
34
100
  label: 'Quotations',
@@ -109,7 +175,7 @@ export const sales = defineModule({
109
175
  'uom',
110
176
  'priceUnit',
111
177
  'discount',
112
- 'tax',
178
+ 'taxes',
113
179
  'subtotal',
114
180
  ], { readonlyWhen: { state: { in: ['done', 'cancel'] } } }),
115
181
  k.group('totals', ['amountUntaxed', 'amountTax', 'amountTotal'], { label: 'Totals' }),
@@ -467,6 +467,16 @@ export declare const SaleOrderLine: import("../server/orm.js").Model<"sale.order
467
467
  readonly hasDefault: true;
468
468
  readonly readonly: false;
469
469
  }>;
470
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
471
+ readonly required: true;
472
+ readonly computed: false;
473
+ readonly inverse: false;
474
+ readonly sequence: false;
475
+ readonly computeName: undefined;
476
+ readonly store: true;
477
+ readonly hasDefault: true;
478
+ readonly readonly: false;
479
+ }>;
470
480
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
471
481
  readonly required: false;
472
482
  readonly computed: false;
@@ -475,7 +485,7 @@ export declare const SaleOrderLine: import("../server/orm.js").Model<"sale.order
475
485
  readonly computeName: undefined;
476
486
  readonly store: true;
477
487
  readonly hasDefault: false;
478
- readonly readonly: false;
488
+ readonly readonly: true;
479
489
  }>;
480
490
  readonly currency: import("../server/orm.js").Many2oneField<"res.currency", {
481
491
  readonly required: false;
@@ -579,6 +589,18 @@ export declare const SaleOrderLine: import("../server/orm.js").Model<"sale.order
579
589
  readonly hasDefault: true;
580
590
  readonly readonly: false;
581
591
  }>;
592
+ /** The line's taxes, applied in order (RFC 0016). */
593
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
594
+ readonly required: true;
595
+ readonly computed: false;
596
+ readonly inverse: false;
597
+ readonly sequence: false;
598
+ readonly computeName: undefined;
599
+ readonly store: true;
600
+ readonly hasDefault: true;
601
+ readonly readonly: false;
602
+ }>;
603
+ /** Before 0.2.0, one tax per line: copied into `taxes` by the migration; dropped later. */
582
604
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
583
605
  readonly required: false;
584
606
  readonly computed: false;
@@ -587,7 +609,7 @@ export declare const SaleOrderLine: import("../server/orm.js").Model<"sale.order
587
609
  readonly computeName: undefined;
588
610
  readonly store: true;
589
611
  readonly hasDefault: false;
590
- readonly readonly: false;
612
+ readonly readonly: true;
591
613
  }>;
592
614
  /** The order's currency, so line amounts are Money in it. */
593
615
  readonly currency: import("../server/orm.js").Many2oneField<"res.currency", {
@@ -1089,6 +1111,16 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1089
1111
  readonly hasDefault: true;
1090
1112
  readonly readonly: false;
1091
1113
  }>;
1114
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
1115
+ readonly required: true;
1116
+ readonly computed: false;
1117
+ readonly inverse: false;
1118
+ readonly sequence: false;
1119
+ readonly computeName: undefined;
1120
+ readonly store: true;
1121
+ readonly hasDefault: true;
1122
+ readonly readonly: false;
1123
+ }>;
1092
1124
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
1093
1125
  readonly required: false;
1094
1126
  readonly computed: false;
@@ -1097,7 +1129,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1097
1129
  readonly computeName: undefined;
1098
1130
  readonly store: true;
1099
1131
  readonly hasDefault: false;
1100
- readonly readonly: false;
1132
+ readonly readonly: true;
1101
1133
  }>;
1102
1134
  readonly currency: import("../server/orm.js").Many2oneField<"res.currency", {
1103
1135
  readonly required: false;
@@ -1201,6 +1233,18 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1201
1233
  readonly hasDefault: true;
1202
1234
  readonly readonly: false;
1203
1235
  }>;
1236
+ /** The line's taxes, applied in order (RFC 0016). */
1237
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
1238
+ readonly required: true;
1239
+ readonly computed: false;
1240
+ readonly inverse: false;
1241
+ readonly sequence: false;
1242
+ readonly computeName: undefined;
1243
+ readonly store: true;
1244
+ readonly hasDefault: true;
1245
+ readonly readonly: false;
1246
+ }>;
1247
+ /** Before 0.2.0, one tax per line: copied into `taxes` by the migration; dropped later. */
1204
1248
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
1205
1249
  readonly required: false;
1206
1250
  readonly computed: false;
@@ -1209,7 +1253,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1209
1253
  readonly computeName: undefined;
1210
1254
  readonly store: true;
1211
1255
  readonly hasDefault: false;
1212
- readonly readonly: false;
1256
+ readonly readonly: true;
1213
1257
  }>;
1214
1258
  /** The order's currency, so line amounts are Money in it. */
1215
1259
  readonly currency: import("../server/orm.js").Many2oneField<"res.currency", {
@@ -1406,6 +1450,16 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1406
1450
  readonly hasDefault: false;
1407
1451
  readonly readonly: false;
1408
1452
  }, string>;
1453
+ readonly state: import("../server/orm.js").CharField<{
1454
+ readonly required: false;
1455
+ readonly computed: false;
1456
+ readonly inverse: false;
1457
+ readonly sequence: false;
1458
+ readonly computeName: undefined;
1459
+ readonly store: true;
1460
+ readonly hasDefault: false;
1461
+ readonly readonly: false;
1462
+ }, string>;
1409
1463
  readonly taxId: import("../server/orm.js").CharField<{
1410
1464
  readonly required: false;
1411
1465
  readonly computed: false;
@@ -1577,6 +1631,16 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1577
1631
  readonly hasDefault: false;
1578
1632
  readonly readonly: false;
1579
1633
  }, string>;
1634
+ readonly state: import("../server/orm.js").CharField<{
1635
+ readonly required: false;
1636
+ readonly computed: false;
1637
+ readonly inverse: false;
1638
+ readonly sequence: false;
1639
+ readonly computeName: undefined;
1640
+ readonly store: true;
1641
+ readonly hasDefault: false;
1642
+ readonly readonly: false;
1643
+ }, string>;
1580
1644
  readonly taxId: import("../server/orm.js").CharField<{
1581
1645
  readonly required: false;
1582
1646
  readonly computed: false;
@@ -1897,6 +1961,26 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1897
1961
  readonly hasDefault: true;
1898
1962
  readonly readonly: false;
1899
1963
  }>;
1964
+ readonly sequence: import("../server/orm.js").ScalarField<"integer", number, number, {
1965
+ readonly required: true;
1966
+ readonly computed: false;
1967
+ readonly inverse: false;
1968
+ readonly sequence: false;
1969
+ readonly computeName: undefined;
1970
+ readonly store: true;
1971
+ readonly hasDefault: true;
1972
+ readonly readonly: false;
1973
+ }>;
1974
+ readonly amountType: import("../server/orm.js").SelectionField<"fixed" | "percent", {
1975
+ readonly required: true;
1976
+ readonly computed: false;
1977
+ readonly inverse: false;
1978
+ readonly sequence: false;
1979
+ readonly computeName: undefined;
1980
+ readonly store: true;
1981
+ readonly hasDefault: true;
1982
+ readonly readonly: false;
1983
+ }>;
1900
1984
  readonly rate: import("../server/orm.js").DecimalField<{
1901
1985
  readonly required: true;
1902
1986
  readonly computed: false;
@@ -1907,6 +1991,46 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1907
1991
  readonly hasDefault: true;
1908
1992
  readonly readonly: false;
1909
1993
  }>;
1994
+ readonly fixedAmount: import("../server/orm.js").DecimalField<{
1995
+ readonly required: true;
1996
+ readonly computed: false;
1997
+ readonly inverse: false;
1998
+ readonly sequence: false;
1999
+ readonly computeName: undefined;
2000
+ readonly store: true;
2001
+ readonly hasDefault: true;
2002
+ readonly readonly: false;
2003
+ }>;
2004
+ readonly affectsBase: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2005
+ readonly required: true;
2006
+ readonly computed: false;
2007
+ readonly inverse: false;
2008
+ readonly sequence: false;
2009
+ readonly computeName: undefined;
2010
+ readonly store: true;
2011
+ readonly hasDefault: true;
2012
+ readonly readonly: false;
2013
+ }>;
2014
+ readonly priceIncluded: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2015
+ readonly required: true;
2016
+ readonly computed: false;
2017
+ readonly inverse: false;
2018
+ readonly sequence: false;
2019
+ readonly computeName: undefined;
2020
+ readonly store: true;
2021
+ readonly hasDefault: true;
2022
+ readonly readonly: false;
2023
+ }>;
2024
+ readonly reverseCharge: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2025
+ readonly required: true;
2026
+ readonly computed: false;
2027
+ readonly inverse: false;
2028
+ readonly sequence: false;
2029
+ readonly computeName: undefined;
2030
+ readonly store: true;
2031
+ readonly hasDefault: true;
2032
+ readonly readonly: false;
2033
+ }>;
1910
2034
  }, {} & import("../server/orm.js").TransitionMethods<{
1911
2035
  readonly name: import("../server/orm.js").CharField<{
1912
2036
  readonly required: true;
@@ -1928,6 +2052,26 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1928
2052
  readonly hasDefault: true;
1929
2053
  readonly readonly: false;
1930
2054
  }>;
2055
+ readonly sequence: import("../server/orm.js").ScalarField<"integer", number, number, {
2056
+ readonly required: true;
2057
+ readonly computed: false;
2058
+ readonly inverse: false;
2059
+ readonly sequence: false;
2060
+ readonly computeName: undefined;
2061
+ readonly store: true;
2062
+ readonly hasDefault: true;
2063
+ readonly readonly: false;
2064
+ }>;
2065
+ readonly amountType: import("../server/orm.js").SelectionField<"fixed" | "percent", {
2066
+ readonly required: true;
2067
+ readonly computed: false;
2068
+ readonly inverse: false;
2069
+ readonly sequence: false;
2070
+ readonly computeName: undefined;
2071
+ readonly store: true;
2072
+ readonly hasDefault: true;
2073
+ readonly readonly: false;
2074
+ }>;
1931
2075
  readonly rate: import("../server/orm.js").DecimalField<{
1932
2076
  readonly required: true;
1933
2077
  readonly computed: false;
@@ -1938,6 +2082,46 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
1938
2082
  readonly hasDefault: true;
1939
2083
  readonly readonly: false;
1940
2084
  }>;
2085
+ readonly fixedAmount: import("../server/orm.js").DecimalField<{
2086
+ readonly required: true;
2087
+ readonly computed: false;
2088
+ readonly inverse: false;
2089
+ readonly sequence: false;
2090
+ readonly computeName: undefined;
2091
+ readonly store: true;
2092
+ readonly hasDefault: true;
2093
+ readonly readonly: false;
2094
+ }>;
2095
+ readonly affectsBase: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2096
+ readonly required: true;
2097
+ readonly computed: false;
2098
+ readonly inverse: false;
2099
+ readonly sequence: false;
2100
+ readonly computeName: undefined;
2101
+ readonly store: true;
2102
+ readonly hasDefault: true;
2103
+ readonly readonly: false;
2104
+ }>;
2105
+ readonly priceIncluded: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2106
+ readonly required: true;
2107
+ readonly computed: false;
2108
+ readonly inverse: false;
2109
+ readonly sequence: false;
2110
+ readonly computeName: undefined;
2111
+ readonly store: true;
2112
+ readonly hasDefault: true;
2113
+ readonly readonly: false;
2114
+ }>;
2115
+ readonly reverseCharge: import("../server/orm.js").ScalarField<"boolean", boolean, boolean, {
2116
+ readonly required: true;
2117
+ readonly computed: false;
2118
+ readonly inverse: false;
2119
+ readonly sequence: false;
2120
+ readonly computeName: undefined;
2121
+ readonly store: true;
2122
+ readonly hasDefault: true;
2123
+ readonly readonly: false;
2124
+ }>;
1941
2125
  }>>, import("../server/orm.js").Model<"product.product", {
1942
2126
  readonly id: import("../server/orm.js").IdField<"product.product">;
1943
2127
  readonly createdAt: import("../server/orm.js").ScalarField<'datetime', Date, Date, {
@@ -2081,6 +2265,26 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2081
2265
  readonly hasDefault: true;
2082
2266
  readonly readonly: false;
2083
2267
  }>;
2268
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
2269
+ readonly required: true;
2270
+ readonly computed: false;
2271
+ readonly inverse: false;
2272
+ readonly sequence: false;
2273
+ readonly computeName: undefined;
2274
+ readonly store: true;
2275
+ readonly hasDefault: true;
2276
+ readonly readonly: false;
2277
+ }>;
2278
+ readonly purchaseTaxes: import("../server/orm.js").Many2manyField<"account.tax", {
2279
+ readonly required: true;
2280
+ readonly computed: false;
2281
+ readonly inverse: false;
2282
+ readonly sequence: false;
2283
+ readonly computeName: undefined;
2284
+ readonly store: true;
2285
+ readonly hasDefault: true;
2286
+ readonly readonly: false;
2287
+ }>;
2084
2288
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
2085
2289
  readonly required: false;
2086
2290
  readonly computed: false;
@@ -2089,7 +2293,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2089
2293
  readonly computeName: undefined;
2090
2294
  readonly store: true;
2091
2295
  readonly hasDefault: false;
2092
- readonly readonly: false;
2296
+ readonly readonly: true;
2093
2297
  }>;
2094
2298
  readonly purchaseTax: import("../server/orm.js").Many2oneField<"account.tax", {
2095
2299
  readonly required: false;
@@ -2099,7 +2303,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2099
2303
  readonly computeName: undefined;
2100
2304
  readonly store: true;
2101
2305
  readonly hasDefault: false;
2102
- readonly readonly: false;
2306
+ readonly readonly: true;
2103
2307
  }>;
2104
2308
  readonly description: import("../server/orm.js").ScalarField<"text", string, string | Readonly<Record<string, string>>, {
2105
2309
  readonly required: false;
@@ -2212,6 +2416,26 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2212
2416
  readonly hasDefault: true;
2213
2417
  readonly readonly: false;
2214
2418
  }>;
2419
+ readonly taxes: import("../server/orm.js").Many2manyField<"account.tax", {
2420
+ readonly required: true;
2421
+ readonly computed: false;
2422
+ readonly inverse: false;
2423
+ readonly sequence: false;
2424
+ readonly computeName: undefined;
2425
+ readonly store: true;
2426
+ readonly hasDefault: true;
2427
+ readonly readonly: false;
2428
+ }>;
2429
+ readonly purchaseTaxes: import("../server/orm.js").Many2manyField<"account.tax", {
2430
+ readonly required: true;
2431
+ readonly computed: false;
2432
+ readonly inverse: false;
2433
+ readonly sequence: false;
2434
+ readonly computeName: undefined;
2435
+ readonly store: true;
2436
+ readonly hasDefault: true;
2437
+ readonly readonly: false;
2438
+ }>;
2215
2439
  readonly tax: import("../server/orm.js").Many2oneField<"account.tax", {
2216
2440
  readonly required: false;
2217
2441
  readonly computed: false;
@@ -2220,7 +2444,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2220
2444
  readonly computeName: undefined;
2221
2445
  readonly store: true;
2222
2446
  readonly hasDefault: false;
2223
- readonly readonly: false;
2447
+ readonly readonly: true;
2224
2448
  }>;
2225
2449
  readonly purchaseTax: import("../server/orm.js").Many2oneField<"account.tax", {
2226
2450
  readonly required: false;
@@ -2230,7 +2454,7 @@ export declare const salesShutro: import("../server/orm.js").Module<readonly [im
2230
2454
  readonly computeName: undefined;
2231
2455
  readonly store: true;
2232
2456
  readonly hasDefault: false;
2233
- readonly readonly: false;
2457
+ readonly readonly: true;
2234
2458
  }>;
2235
2459
  readonly description: import("../server/orm.js").ScalarField<"text", string, string | Readonly<Record<string, string>>, {
2236
2460
  readonly required: false;
@@ -5,9 +5,14 @@
5
5
  */
6
6
  import { contactsShutro } from '../contacts/index.js';
7
7
  import { messagingShutro } from '../messaging/index.js';
8
- import { productShutro } from '../product/index.js';
8
+ import { computeTaxes, productShutro, taxRule } from '../product/index.js';
9
9
  import { orm } from '../server/orm.js';
10
10
  import { check, compute, defineModel, defineModule, f, onchange, records, } from './.kosh/bindings.js';
11
+ /** A line's taxes through the engine (RFC 0016): its amount after discount, its taxes in order. */
12
+ function taxesOf(l) {
13
+ const amount = l.quantity.times(l.priceUnit).times(orm.toDecimal(100).minus(l.discount)).div(100);
14
+ return computeTaxes(amount, l.quantity, [...l.taxes].map(taxRule));
15
+ }
11
16
  const amounts = compute('sale.order', ['amountUntaxed', 'amountTax', 'amountTotal'], ['lines.subtotal', 'lines.taxAmount'], (orders) => {
12
17
  for (const o of orders) {
13
18
  // A new order has no currency until its partner is chosen: no amounts yet.
@@ -124,7 +129,10 @@ export const SaleOrderLine = defineModel('sale.order.line', {
124
129
  priceUnit: f.decimal({ scale: 2, required: true, default: '0', label: 'Unit price' }),
125
130
  /** Percent off the unit price (0–100). */
126
131
  discount: f.decimal({ scale: 2, required: true, default: '0', label: 'Discount %' }),
127
- tax: f.many2one('account.tax'),
132
+ /** The line's taxes, applied in order (RFC 0016). */
133
+ taxes: f.many2many('account.tax'),
134
+ /** Before 0.2.0, one tax per line: copied into `taxes` by the migration; dropped later. */
135
+ tax: f.many2one('account.tax', { label: 'Tax (before 0.2.0)', readonly: true }),
128
136
  /** The order's currency, so line amounts are Money in it. */
129
137
  currency: f.many2one('res.currency', {
130
138
  store: true,
@@ -136,33 +144,54 @@ export const SaleOrderLine = defineModel('sale.order.line', {
136
144
  subtotal: f.monetary({
137
145
  currency: 'currency',
138
146
  store: true,
139
- ...compute('sale.order.line', 'subtotal', ['quantity', 'priceUnit', 'discount'], (lines) => {
147
+ ...compute('sale.order.line', 'subtotal', [
148
+ 'quantity',
149
+ 'priceUnit',
150
+ 'discount',
151
+ 'taxes.rate',
152
+ 'taxes.sequence',
153
+ 'taxes.amountType',
154
+ 'taxes.fixedAmount',
155
+ 'taxes.affectsBase',
156
+ 'taxes.priceIncluded',
157
+ 'taxes.reverseCharge',
158
+ ], (lines) => {
140
159
  for (const l of lines)
141
- l.subtotal = l.quantity
142
- .times(l.priceUnit)
143
- .times(orm.toDecimal(100).minus(l.discount))
144
- .div(100);
160
+ l.subtotal = taxesOf(l).untaxed;
145
161
  }),
146
162
  }),
147
163
  taxAmount: f.monetary({
148
164
  currency: 'currency',
149
165
  store: true,
150
166
  label: 'Tax',
151
- ...compute('sale.order.line', 'taxAmount', ['subtotal', 'tax.rate'], (lines) => {
167
+ ...compute('sale.order.line', 'taxAmount', [
168
+ 'quantity',
169
+ 'priceUnit',
170
+ 'discount',
171
+ 'taxes.rate',
172
+ 'taxes.sequence',
173
+ 'taxes.amountType',
174
+ 'taxes.fixedAmount',
175
+ 'taxes.affectsBase',
176
+ 'taxes.priceIncluded',
177
+ 'taxes.reverseCharge',
178
+ ], (lines) => {
152
179
  for (const l of lines)
153
- l.taxAmount = (l.subtotal?.amount ?? orm.toDecimal(0)).times(l.tax?.rate ?? 0);
180
+ l.taxAmount = taxesOf(l).taxTotal;
154
181
  }),
155
182
  }),
156
183
  },
157
184
  onchange: {
158
- product: onchange('sale.order.line', async (d) => {
185
+ product: onchange('sale.order.line', async (d, env) => {
159
186
  const p = await d.record('product');
160
187
  if (p === null)
161
188
  return;
162
189
  d.set('name', p.name);
163
190
  d.set('priceUnit', p.listPrice);
164
191
  d.set('uom', p.uom?.id ?? null);
165
- d.set('tax', p.tax?.id ?? null);
192
+ // The product's taxes, loaded (a draft's related record has its plain fields only).
193
+ const [taxed] = await env.model('product.product').browse([p.id]).load({ taxes: true });
194
+ d.set('taxes', { set: [...(taxed?.taxes.ids ?? [])] });
166
195
  }),
167
196
  },
168
197
  constraints: {
@@ -178,7 +207,17 @@ export const SaleOrderLine = defineModel('sale.order.line', {
178
207
  });
179
208
  export const salesShutro = defineModule({
180
209
  name: 'sales',
181
- version: '0.1.0',
210
+ version: '0.2.0',
211
+ migrations: [
212
+ {
213
+ version: '0.2.0',
214
+ description: 'Several taxes per line: the single tax of each line becomes its first',
215
+ up: async (ctx) => {
216
+ await ctx.sql `INSERT INTO sale_order_line_account_tax_rel (sale_order_line_id, account_tax_id)
217
+ SELECT id, tax_id FROM sale_order_line WHERE tax_id IS NOT NULL ON CONFLICT DO NOTHING`;
218
+ },
219
+ },
220
+ ],
182
221
  depends: [contactsShutro, productShutro, messagingShutro],
183
222
  groups: [
184
223
  orm.defineGroup({
@@ -20,6 +20,8 @@ const ResCompany = defineModel('res.company', {
20
20
  fields: {
21
21
  name: f.char({ required: true }),
22
22
  currency: f.many2one('res.currency', { required: true }),
23
+ /** How numbers are grouped on screen and in documents (RFC 0016): 1,234,567 or 12,34,567 (lakh and crore). */
24
+ numberGrouping: f.selection({ international: 'International (1,234,567)', lakh: 'Lakh and crore (12,34,567)' }, { required: true, default: 'international', label: 'Number grouping' }),
23
25
  },
24
26
  });
25
27
  const readRows = async (env, model, ids, keys) => env.asSuperuser({ reason: 'check administrators' }, async (su) => {
@@ -37,6 +37,8 @@ export interface UserInfo {
37
37
  readonly name: string;
38
38
  readonly company: number | null;
39
39
  readonly companies: readonly number[];
40
+ /** The number grouping of each of the user's companies (RFC 0016): `lakh` or `international`. */
41
+ readonly groupings: Readonly<Record<string, 'international' | 'lakh'>>;
40
42
  readonly twoFactorEnabled: boolean;
41
43
  /** Holds an administrator role (in some company). */
42
44
  readonly administrator: boolean;