shutro-erp 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +74 -67
  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 +1380 -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 +172 -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/assistant/routes.js +1 -1
  27. package/dist/automation/engine.js +1 -1
  28. package/dist/automation/web/messages.js +2 -2
  29. package/dist/cli/apps.d.ts +1 -1
  30. package/dist/cli/apps.js +14 -14
  31. package/dist/cli/bin.js +3 -0
  32. package/dist/cli/index.js +11 -11
  33. package/dist/cli/init.d.ts +1 -1
  34. package/dist/cli/init.js +224 -78
  35. package/dist/cli/launch.d.ts +16 -0
  36. package/dist/cli/launch.js +138 -0
  37. package/dist/cli/project.d.ts +7 -7
  38. package/dist/cli/project.js +25 -25
  39. package/dist/cli/prompt.d.ts +35 -0
  40. package/dist/cli/prompt.js +48 -2
  41. package/dist/cli/templates.d.ts +14 -6
  42. package/dist/cli/templates.js +61 -34
  43. package/dist/cli/tui.d.ts +7 -0
  44. package/dist/cli/tui.js +254 -0
  45. package/dist/cli/ui.d.ts +21 -8
  46. package/dist/cli/ui.js +65 -19
  47. package/dist/contacts/.kosh/bindings.js +1 -1
  48. package/dist/contacts/.kosh/schema.d.ts +33 -1
  49. package/dist/contacts/index.js +24 -1
  50. package/dist/contacts/module.d.ts +42 -0
  51. package/dist/contacts/module.js +3 -1
  52. package/dist/import/convert.d.ts +55 -0
  53. package/dist/import/convert.js +224 -0
  54. package/dist/import/csv.d.ts +28 -0
  55. package/dist/import/csv.js +115 -0
  56. package/dist/import/index.d.ts +33 -0
  57. package/dist/import/index.js +60 -0
  58. package/dist/import/mapping.d.ts +15 -0
  59. package/dist/import/mapping.js +33 -0
  60. package/dist/import/match.d.ts +24 -0
  61. package/dist/import/match.js +60 -0
  62. package/dist/import/models.d.ts +2 -0
  63. package/dist/import/models.js +102 -0
  64. package/dist/import/routes.d.ts +13 -0
  65. package/dist/import/routes.js +392 -0
  66. package/dist/import/run.d.ts +131 -0
  67. package/dist/import/run.js +568 -0
  68. package/dist/import/service.d.ts +153 -0
  69. package/dist/import/service.js +367 -0
  70. package/dist/import/web/ImportButton.d.ts +7 -0
  71. package/dist/import/web/ImportButton.js +31 -0
  72. package/dist/import/web/ImportWizard.d.ts +75 -0
  73. package/dist/import/web/ImportWizard.js +217 -0
  74. package/dist/import/web/index.d.ts +13 -0
  75. package/dist/import/web/index.js +18 -0
  76. package/dist/import/web/messages.d.ts +2 -0
  77. package/dist/import/web/messages.js +139 -0
  78. package/dist/import/worker.d.ts +3 -0
  79. package/dist/import/worker.js +52 -0
  80. package/dist/import/xlsx.d.ts +20 -0
  81. package/dist/import/xlsx.js +192 -0
  82. package/dist/l10n_bd/.kosh/bindings.d.ts +22 -0
  83. package/dist/l10n_bd/.kosh/bindings.js +15 -0
  84. package/dist/l10n_bd/.kosh/schema.d.ts +1311 -0
  85. package/dist/l10n_bd/documents.d.ts +8 -0
  86. package/dist/l10n_bd/documents.js +183 -0
  87. package/dist/l10n_bd/index.d.ts +8 -0
  88. package/dist/l10n_bd/index.js +156 -0
  89. package/dist/l10n_bd/module.d.ts +5705 -0
  90. package/dist/l10n_bd/module.js +163 -0
  91. package/dist/l10n_bd/mushak.d.ts +67 -0
  92. package/dist/l10n_bd/mushak.js +139 -0
  93. package/dist/l10n_bd/routes.d.ts +3 -0
  94. package/dist/l10n_bd/routes.js +205 -0
  95. package/dist/l10n_bd/web/MushakPage.d.ts +7 -0
  96. package/dist/l10n_bd/web/MushakPage.js +53 -0
  97. package/dist/l10n_bd/web/index.d.ts +13 -0
  98. package/dist/l10n_bd/web/index.js +18 -0
  99. package/dist/l10n_bd/web/messages.d.ts +2 -0
  100. package/dist/l10n_bd/web/messages.js +101 -0
  101. package/dist/l10n_in/.kosh/bindings.d.ts +22 -0
  102. package/dist/l10n_in/.kosh/bindings.js +15 -0
  103. package/dist/l10n_in/.kosh/schema.d.ts +1373 -0
  104. package/dist/l10n_in/documents.d.ts +8 -0
  105. package/dist/l10n_in/documents.js +114 -0
  106. package/dist/l10n_in/einvoice.d.ts +253 -0
  107. package/dist/l10n_in/einvoice.js +485 -0
  108. package/dist/l10n_in/gstr.d.ts +109 -0
  109. package/dist/l10n_in/gstr.js +430 -0
  110. package/dist/l10n_in/index.d.ts +20 -0
  111. package/dist/l10n_in/index.js +232 -0
  112. package/dist/l10n_in/module.d.ts +7823 -0
  113. package/dist/l10n_in/module.js +427 -0
  114. package/dist/l10n_in/routes-einvoice.d.ts +15 -0
  115. package/dist/l10n_in/routes-einvoice.js +124 -0
  116. package/dist/l10n_in/routes.d.ts +17 -0
  117. package/dist/l10n_in/routes.js +282 -0
  118. package/dist/l10n_in/web/EInvoicePanel.d.ts +8 -0
  119. package/dist/l10n_in/web/EInvoicePanel.js +63 -0
  120. package/dist/l10n_in/web/GstPage.d.ts +7 -0
  121. package/dist/l10n_in/web/GstPage.js +57 -0
  122. package/dist/l10n_in/web/GstSettings.d.ts +10 -0
  123. package/dist/l10n_in/web/GstSettings.js +36 -0
  124. package/dist/l10n_in/web/index.d.ts +15 -0
  125. package/dist/l10n_in/web/index.js +22 -0
  126. package/dist/l10n_in/web/messages.d.ts +2 -0
  127. package/dist/l10n_in/web/messages.js +166 -0
  128. package/dist/mail/index.js +2 -2
  129. package/dist/modules-sdk/demo.d.ts +2 -0
  130. package/dist/modules-sdk/demo.js +7 -3
  131. package/dist/modules-sdk/index.d.ts +2 -2
  132. package/dist/modules-sdk/index.js +1 -1
  133. package/dist/modules-sdk/loader.js +2 -2
  134. package/dist/modules-sdk/manifest.d.ts +132 -0
  135. package/dist/modules-sdk/manifest.js +22 -0
  136. package/dist/notify/inbox.js +21 -0
  137. package/dist/notify/messages.js +7 -3
  138. package/dist/notify/models.d.ts +1 -1
  139. package/dist/notify/models.js +2 -0
  140. package/dist/notify/routes.js +1 -1
  141. package/dist/notify/web/messages.js +5 -1
  142. package/dist/portal/.kosh/bindings.d.ts +22 -0
  143. package/dist/portal/.kosh/bindings.js +15 -0
  144. package/dist/portal/.kosh/schema.d.ts +376 -0
  145. package/dist/portal/announce.d.ts +16 -0
  146. package/dist/portal/announce.js +117 -0
  147. package/dist/portal/documents.d.ts +17 -0
  148. package/dist/portal/documents.js +61 -0
  149. package/dist/portal/index.d.ts +20 -0
  150. package/dist/portal/index.js +65 -0
  151. package/dist/portal/module.d.ts +1405 -0
  152. package/dist/portal/module.js +84 -0
  153. package/dist/portal/routes-documents.d.ts +2 -0
  154. package/dist/portal/routes-documents.js +391 -0
  155. package/dist/portal/routes.d.ts +13 -0
  156. package/dist/portal/routes.js +313 -0
  157. package/dist/portal/rules.d.ts +11 -0
  158. package/dist/portal/rules.js +25 -0
  159. package/dist/portal/web/PortalAccess.d.ts +8 -0
  160. package/dist/portal/web/PortalAccess.js +66 -0
  161. package/dist/portal/web/index.d.ts +12 -0
  162. package/dist/portal/web/index.js +17 -0
  163. package/dist/portal/web/messages.d.ts +2 -0
  164. package/dist/portal/web/messages.js +33 -0
  165. package/dist/product/.kosh/bindings.js +1 -1
  166. package/dist/product/.kosh/schema.d.ts +42 -3
  167. package/dist/product/demo.js +6 -5
  168. package/dist/product/index.d.ts +1 -0
  169. package/dist/product/index.js +17 -4
  170. package/dist/product/module.d.ts +346 -14
  171. package/dist/product/module.js +48 -18
  172. package/dist/product/taxes.d.ts +59 -0
  173. package/dist/product/taxes.js +61 -0
  174. package/dist/project/.kosh/bindings.js +1 -1
  175. package/dist/project/.kosh/schema.d.ts +33 -1
  176. package/dist/project/module.d.ts +20 -0
  177. package/dist/project_portal/.kosh/bindings.d.ts +22 -0
  178. package/dist/project_portal/.kosh/bindings.js +15 -0
  179. package/dist/project_portal/.kosh/schema.d.ts +701 -0
  180. package/dist/project_portal/index.d.ts +9 -0
  181. package/dist/project_portal/index.js +42 -0
  182. package/dist/project_portal/module.d.ts +2142 -0
  183. package/dist/project_portal/module.js +22 -0
  184. package/dist/purchase/.kosh/bindings.js +1 -1
  185. package/dist/purchase/.kosh/schema.d.ts +45 -4
  186. package/dist/purchase/demo.js +3 -3
  187. package/dist/purchase/index.js +1 -1
  188. package/dist/purchase/module.d.ts +232 -8
  189. package/dist/purchase/module.js +51 -9
  190. package/dist/purchase_account/.kosh/bindings.js +1 -1
  191. package/dist/purchase_account/.kosh/schema.d.ts +174 -10
  192. package/dist/purchase_account/module.d.ts +2008 -641
  193. package/dist/purchase_account/module.js +2 -2
  194. package/dist/purchase_portal/.kosh/bindings.d.ts +22 -0
  195. package/dist/purchase_portal/.kosh/bindings.js +15 -0
  196. package/dist/purchase_portal/.kosh/schema.d.ts +852 -0
  197. package/dist/purchase_portal/index.d.ts +9 -0
  198. package/dist/purchase_portal/index.js +53 -0
  199. package/dist/purchase_portal/module.d.ts +2758 -0
  200. package/dist/purchase_portal/module.js +18 -0
  201. package/dist/reporting/documents.js +20 -3
  202. package/dist/reporting/index.d.ts +1 -0
  203. package/dist/reporting/index.js +1 -0
  204. package/dist/reporting/routes-documents.js +9 -2
  205. package/dist/reporting/words.d.ts +11 -0
  206. package/dist/reporting/words.js +229 -0
  207. package/dist/sale_account/.kosh/bindings.js +1 -1
  208. package/dist/sale_account/.kosh/schema.d.ts +174 -10
  209. package/dist/sale_account/module.d.ts +2008 -641
  210. package/dist/sale_account/module.js +3 -3
  211. package/dist/sale_portal/.kosh/bindings.d.ts +22 -0
  212. package/dist/sale_portal/.kosh/bindings.js +15 -0
  213. package/dist/sale_portal/.kosh/schema.d.ts +766 -0
  214. package/dist/sale_portal/index.d.ts +9 -0
  215. package/dist/sale_portal/index.js +71 -0
  216. package/dist/sale_portal/module.d.ts +2718 -0
  217. package/dist/sale_portal/module.js +19 -0
  218. package/dist/sale_project/.kosh/bindings.js +1 -1
  219. package/dist/sale_project/.kosh/schema.d.ts +45 -4
  220. package/dist/sale_project/module.d.ts +390 -10
  221. package/dist/sales/.kosh/bindings.js +1 -1
  222. package/dist/sales/.kosh/schema.d.ts +45 -4
  223. package/dist/sales/demo.js +3 -3
  224. package/dist/sales/index.js +67 -1
  225. package/dist/sales/module.d.ts +232 -8
  226. package/dist/sales/module.js +51 -12
  227. package/dist/server/api/client.js +1 -1
  228. package/dist/server/api/openapi.js +17 -1
  229. package/dist/server/apps.js +5 -5
  230. package/dist/server/auth/api-keys.d.ts +13 -0
  231. package/dist/server/auth/api-keys.js +65 -0
  232. package/dist/server/auth/base.js +26 -0
  233. package/dist/server/auth/crypto.js +1 -1
  234. package/dist/server/auth/scopes.d.ts +12 -0
  235. package/dist/server/auth/scopes.js +51 -0
  236. package/dist/server/auth/service.d.ts +59 -1
  237. package/dist/server/auth/service.js +236 -6
  238. package/dist/server/demo.d.ts +1 -1
  239. package/dist/server/demo.js +1 -1
  240. package/dist/server/env-names.d.ts +2 -0
  241. package/dist/server/env-names.js +16 -0
  242. package/dist/server/host.js +5 -1
  243. package/dist/server/identity.d.ts +7 -0
  244. package/dist/server/index.d.ts +2 -1
  245. package/dist/server/index.js +2 -1
  246. package/dist/server/registry.js +1 -1
  247. package/dist/server/server.d.ts +5 -0
  248. package/dist/server/server.js +71 -6
  249. package/dist/settings/routes.js +1 -1
  250. package/dist/settings/web/ApiKeysPage.d.ts +8 -0
  251. package/dist/settings/web/ApiKeysPage.js +70 -0
  252. package/dist/settings/web/index.d.ts +1 -0
  253. package/dist/settings/web/index.js +4 -0
  254. package/dist/settings/web/messages.js +62 -0
  255. package/dist/storage/routes.js +1 -1
  256. package/dist/studio/build.js +1 -1
  257. package/dist/studio/generate.js +4 -4
  258. package/dist/studio/module.js +5 -5
  259. package/dist/studio/web/messages.js +2 -2
  260. package/dist/ui/api.d.ts +2 -0
  261. package/dist/ui/app.js +27 -4
  262. package/dist/ui/context.d.ts +3 -1
  263. package/dist/ui/context.js +2 -2
  264. package/dist/ui/i18n/messages.js +2 -2
  265. package/dist/ui/index.d.ts +1 -1
  266. package/dist/ui/index.js +1 -1
  267. package/dist/ui/lib/format.d.ts +2 -0
  268. package/dist/ui/lib/format.js +3 -1
  269. package/dist/ui/pwa/worker.js +2 -2
  270. package/dist/ui/registry.d.ts +15 -0
  271. package/dist/ui/registry.js +20 -0
  272. package/dist/ui/views/FormView.js +19 -6
  273. package/package.json +52 -4
@@ -150,6 +150,8 @@ export async function readSettings(env) {
150
150
  chartTemplate: c?.chartTemplate ?? null,
151
151
  lockDate: c?.lockDate ?? null,
152
152
  taxLockDate: c?.taxLockDate ?? null,
153
+ country: c?.country ?? null,
154
+ state: c?.state ?? null,
153
155
  accounts: {
154
156
  receivable: c?.accountReceivable?.id ?? null,
155
157
  payable: c?.accountPayable?.id ?? null,
@@ -159,7 +161,7 @@ export async function readSettings(env) {
159
161
  },
160
162
  };
161
163
  }
162
- /** The lock dates (managers). */
164
+ /** The lock dates, the company's country and state (managers). */
163
165
  export async function updateSettings(env, input) {
164
166
  needs(env, 'account.settings', 'Changing the accounting settings');
165
167
  await env.asSuperuser({ reason: 'accounting settings' }, (su) => su
@@ -168,6 +170,8 @@ export async function updateSettings(env, input) {
168
170
  .write({
169
171
  ...(input.lockDate === undefined ? {} : { lockDate: input.lockDate }),
170
172
  ...(input.taxLockDate === undefined ? {} : { taxLockDate: input.taxLockDate }),
173
+ ...(input.country === undefined ? {} : { country: input.country }),
174
+ ...(input.state === undefined ? {} : { state: input.state }),
171
175
  }));
172
176
  return readSettings(env);
173
177
  }
@@ -3,7 +3,7 @@
3
3
  * and vendor bills with VAT, one invoice paid in full and one in part, so the aging, the trial balance and the
4
4
  * reconciliation page have something to show.
5
5
  */
6
- import { idByName } from '../modules-sdk/index.js';
6
+ import { idByName, maybeIdByName } from '../modules-sdk/index.js';
7
7
  import { orm } from '../server/orm.js';
8
8
  import { installChart, installedChart } from './chart.js';
9
9
  import { registerPayment } from './payments.js';
@@ -49,7 +49,7 @@ const BILLS = [
49
49
  },
50
50
  ];
51
51
  async function document(env, context, kind, journal, currency, doc) {
52
- const vat = await idByName(env, 'account.tax', kind === 'out_invoice' ? 'VAT 15%' : 'VAT 15% (purchases)');
52
+ const vat = await maybeIdByName(env, 'account.tax', kind === 'out_invoice' ? 'VAT 15%' : 'VAT 15% (purchases)');
53
53
  const lines = [];
54
54
  for (const l of doc.lines)
55
55
  lines.push({
@@ -58,7 +58,7 @@ async function document(env, context, kind, journal, currency, doc) {
58
58
  name: l.product,
59
59
  quantity: l.quantity,
60
60
  priceUnit: l.price,
61
- tax: orm.asId(vat, 'account.tax'),
61
+ taxes: { set: vat === null ? [] : [orm.asId(vat, 'account.tax')] },
62
62
  });
63
63
  const created = await env.model('account.move').create([
64
64
  {
@@ -77,10 +77,11 @@ async function document(env, context, kind, journal, currency, doc) {
77
77
  export async function accountingDemo(context) {
78
78
  const chart = await context.system('chart of accounts', async (raw) => {
79
79
  const env = raw;
80
+ // A localization (l10n_bd) installs its chart with the app; without one, the generic chart.
80
81
  if ((await installedChart(env)) !== null)
81
82
  return 'the installed chart';
82
- await installChart(env, 'bd');
83
- return 'the Bangladesh chart';
83
+ await installChart(env, 'generic');
84
+ return 'the generic chart';
84
85
  });
85
86
  const journal = async (env, code) => {
86
87
  const found = await env.model('account.journal').search({ where: { code }, limit: 1 });
@@ -1,9 +1,9 @@
1
1
  import { defineModule } from '../modules-sdk/index.js';
2
2
  import type { Env } from './.kosh/bindings.js';
3
- export { CHARTS, type ChartInstalled, ChartInstalledError, type ChartTemplate, installChart, installedChart, installImmutability, } from './chart.js';
3
+ export { type AccountSpec, CHARTS, type Chart, type ChartInstalled, ChartInstalledError, type ChartTemplate, COMMON_ACCOUNTS, chartsOf, DEFAULT_ACCOUNTS, type Defaults, installChart, installedChart, installImmutability, installLocalization, type LocalizationInstalled, localizationsOf, } from './chart.js';
4
4
  export { type AgingRow, agingReport, type Closed, closeFiscalYear, readSettings, reopenFiscalYear, type Settings, updateSettings, } from './closing.js';
5
- export { ACCOUNT_TYPES, AccountAccount, AccountFiscalYear, AccountFullReconcile, AccountJournal, AccountMove, AccountMoveLine, AccountPartialReconcile, AccountPayment, AccountPaymentTerm, AccountPaymentTermLine, type AccountType, accountingShutro, addDays, CompanyAccounting, JOURNAL_TYPES, type JournalType, MOVE_KINDS, type MoveKind, NUMBER_FIELD, REVERSED_KIND, ResCurrencyRate, TaxAccounts, } from './module.js';
6
- export { openLines, type ReconcileResult, type RegisterInput, reconcile, registerPayment, unreconcile, } from './payments.js';
5
+ export { ACCOUNT_TYPES, AccountAccount, AccountFiscalYear, AccountFullReconcile, AccountJournal, AccountMove, AccountMoveLine, AccountPartialReconcile, AccountPayment, AccountPaymentTerm, AccountPaymentTermLine, AccountPaymentWithholding, AccountTaxComponent, type AccountType, AccountWithholding, accountingShutro, addDays, CompanyAccounting, fiscalYearOf, isRefund, JOURNAL_TYPES, type JournalType, MOVE_KINDS, type MoveKind, NUMBER_FIELD, owedToUs, PartnerWithholdings, REVERSED_KIND, ResBankAccount, ResCurrencyRate, type SupplyScope, supplyScope, TaxAccounts, } from './module.js';
6
+ export { openLines, proposeWithholdings, type ReconcileResult, type RegisterInput, reconcile, registerPayment, unreconcile, type WithholdingProposal, } from './payments.js';
7
7
  export interface Reversed {
8
8
  readonly id: number;
9
9
  readonly name: string;
@@ -18,16 +18,16 @@ import { permissions } from '../permissions/index.js';
18
18
  import { product } from '../product/index.js';
19
19
  import { reporting } from '../reporting/index.js';
20
20
  import { HttpProblem } from '../server/index.js';
21
- import { action, form, list, pivot, search } from '../views/index.js';
22
- import { CHARTS, ChartInstalledError, installChart, installedChart, installImmutability, } from './chart.js';
21
+ import { action, field, form, list, pivot, search } from '../views/index.js';
22
+ import { ChartInstalledError, chartsOf, installChart, installedChart, installImmutability, installLocalization, localizationsOf, } from './chart.js';
23
23
  import { agingReport, closeFiscalYear, readSettings, reopenFiscalYear, updateSettings, } from './closing.js';
24
24
  import { accountingDemo } from './demo.js';
25
- import { AccountAccount, AccountFiscalYear, AccountJournal, AccountMove, AccountMoveLine, AccountPayment, AccountPaymentTerm, accountingShutro, REVERSED_KIND, ResCurrencyRate, } from './module.js';
26
- import { openLines, reconcile, registerPayment, unreconcile } from './payments.js';
27
- export { CHARTS, ChartInstalledError, installChart, installedChart, installImmutability, } from './chart.js';
25
+ import { AccountAccount, AccountFiscalYear, AccountJournal, AccountMove, AccountMoveLine, AccountPayment, AccountPaymentTerm, AccountWithholding, accountingShutro, REVERSED_KIND, ResBankAccount, ResCurrencyRate, } from './module.js';
26
+ import { openLines, proposeWithholdings, reconcile, registerPayment, unreconcile, } from './payments.js';
27
+ export { CHARTS, ChartInstalledError, COMMON_ACCOUNTS, chartsOf, DEFAULT_ACCOUNTS, installChart, installedChart, installImmutability, installLocalization, localizationsOf, } from './chart.js';
28
28
  export { agingReport, closeFiscalYear, readSettings, reopenFiscalYear, updateSettings, } from './closing.js';
29
- export { ACCOUNT_TYPES, AccountAccount, AccountFiscalYear, AccountFullReconcile, AccountJournal, AccountMove, AccountMoveLine, AccountPartialReconcile, AccountPayment, AccountPaymentTerm, AccountPaymentTermLine, accountingShutro, addDays, CompanyAccounting, JOURNAL_TYPES, MOVE_KINDS, NUMBER_FIELD, REVERSED_KIND, ResCurrencyRate, TaxAccounts, } from './module.js';
30
- export { openLines, reconcile, registerPayment, unreconcile, } from './payments.js';
29
+ export { ACCOUNT_TYPES, AccountAccount, AccountFiscalYear, AccountFullReconcile, AccountJournal, AccountMove, AccountMoveLine, AccountPartialReconcile, AccountPayment, AccountPaymentTerm, AccountPaymentTermLine, AccountPaymentWithholding, AccountTaxComponent, AccountWithholding, accountingShutro, addDays, CompanyAccounting, fiscalYearOf, isRefund, JOURNAL_TYPES, MOVE_KINDS, NUMBER_FIELD, owedToUs, PartnerWithholdings, REVERSED_KIND, ResBankAccount, ResCurrencyRate, supplyScope, TaxAccounts, } from './module.js';
30
+ export { openLines, proposeWithholdings, reconcile, registerPayment, unreconcile, } from './payments.js';
31
31
  /**
32
32
  * Reverses a posted entry: a new entry in the same journal with every line negated, linked both ways, posted when
33
33
  * asked. The original is never touched beyond its `reversedBy` link.
@@ -35,7 +35,7 @@ export { openLines, reconcile, registerPayment, unreconcile, } from './payments.
35
35
  export async function reverseMove(env, id, options = {}) {
36
36
  const moves = env.model('account.move');
37
37
  const [m] = await moves.browse([id]).load({
38
- lines: { account: true, partner: true, product: true, tax: true },
38
+ lines: { account: true, partner: true, product: true, taxes: true },
39
39
  journal: true,
40
40
  partner: true,
41
41
  currency: true,
@@ -85,7 +85,7 @@ export async function reverseMove(env, id, options = {}) {
85
85
  quantity: l.quantity,
86
86
  priceUnit: l.priceUnit,
87
87
  discount: l.discount,
88
- tax: l.tax?.id ?? null,
88
+ taxes: { set: [...l.taxes].map((t) => t.id) },
89
89
  debit: '0',
90
90
  credit: '0',
91
91
  })),
@@ -103,7 +103,6 @@ export async function reverseMove(env, id, options = {}) {
103
103
  state: reversal?.state ?? 'draft',
104
104
  };
105
105
  }
106
- const TEMPLATES = Object.keys(CHARTS);
107
106
  export const accounting = defineModule({
108
107
  shutro: accountingShutro,
109
108
  kosh: '^0.1.0',
@@ -134,14 +133,14 @@ export const accounting = defineModule({
134
133
  label: 'Customer invoices',
135
134
  views: ['list', 'form'],
136
135
  viewIds: { list: 'account.move.invoice.list', form: 'account.move.invoice.form' },
137
- domain: { kind: { in: ['out_invoice', 'out_refund'] } },
136
+ domain: { kind: { in: ['out_invoice', 'out_refund', 'out_debit'] } },
138
137
  defaults: { kind: 'out_invoice' },
139
138
  }),
140
139
  'accounting.action_vendor_bills': action(AccountMove, {
141
140
  label: 'Vendor bills',
142
141
  views: ['list', 'form'],
143
142
  viewIds: { list: 'account.move.invoice.list', form: 'account.move.invoice.form' },
144
- domain: { kind: { in: ['in_invoice', 'in_refund'] } },
143
+ domain: { kind: { in: ['in_invoice', 'in_refund', 'in_debit'] } },
145
144
  defaults: { kind: 'in_invoice' },
146
145
  }),
147
146
  'accounting.action_moves': action(AccountMove, {
@@ -242,6 +241,14 @@ export const accounting = defineModule({
242
241
  label: 'Fiscal years',
243
242
  views: ['list', 'form'],
244
243
  }),
244
+ 'accounting.action_withholdings': action(AccountWithholding, {
245
+ label: 'Withholdings',
246
+ views: ['list', 'form'],
247
+ }),
248
+ 'accounting.action_banks': action(ResBankAccount, {
249
+ label: 'Bank accounts',
250
+ views: ['list', 'form'],
251
+ }),
245
252
  'accounting.action_rates': action(ResCurrencyRate, {
246
253
  label: 'Currency rates',
247
254
  views: ['list', 'form'],
@@ -368,6 +375,20 @@ export const accounting = defineModule({
368
375
  action: 'accounting.action_fiscal_years',
369
376
  sequence: 3,
370
377
  },
378
+ {
379
+ id: 'accounting.withholdings',
380
+ parent: 'accounting.configuration',
381
+ label: 'Withholdings',
382
+ action: 'accounting.action_withholdings',
383
+ sequence: 2.5,
384
+ },
385
+ {
386
+ id: 'accounting.banks',
387
+ parent: 'accounting.configuration',
388
+ label: 'Bank accounts',
389
+ action: 'accounting.action_banks',
390
+ sequence: 3.5,
391
+ },
371
392
  {
372
393
  id: 'accounting.rates',
373
394
  parent: 'accounting.configuration',
@@ -398,7 +419,85 @@ export const accounting = defineModule({
398
419
  ],
399
420
  },
400
421
  ],
422
+ // Which withholdings apply to a contact (VDS, TDS), next to their roles (RFC 0016).
423
+ viewExtensions: [
424
+ // A tax's accounts, reverse charge's payable account, and its components (CGST / SGST / IGST).
425
+ {
426
+ view: 'account.tax.form',
427
+ operations: [
428
+ {
429
+ insertAfter: 'amount',
430
+ nodes: [
431
+ {
432
+ kind: 'group',
433
+ id: 'tax_accounts',
434
+ children: [
435
+ field({ field: 'account', id: 'tax_account' }),
436
+ field({ field: 'refundAccount', id: 'tax_refund_account' }),
437
+ field({ field: 'reverseChargeAccount', id: 'tax_reverse_charge_account' }),
438
+ ],
439
+ },
440
+ {
441
+ kind: 'lines',
442
+ id: 'components',
443
+ field: 'components',
444
+ columns: [
445
+ field({ field: 'name', id: 'component_name' }),
446
+ field({ field: 'code', id: 'component_code' }),
447
+ field({ field: 'share', id: 'component_share' }),
448
+ field({ field: 'scope', id: 'component_scope' }),
449
+ field({ field: 'account', id: 'component_account' }),
450
+ field({ field: 'refundAccount', id: 'component_refund_account' }),
451
+ field({ field: 'reverseChargeAccount', id: 'component_reverse_charge_account' }),
452
+ ],
453
+ },
454
+ ],
455
+ },
456
+ ],
457
+ },
458
+ {
459
+ view: 'res.partner.form',
460
+ operations: [
461
+ { append: 'roles', nodes: [field({ field: 'withholdings', id: 'withholdings' })] },
462
+ ],
463
+ },
464
+ ],
401
465
  documents: [
466
+ {
467
+ key: 'withholding_certificate',
468
+ name: 'Withholding certificate',
469
+ model: 'account.payment',
470
+ layout: {
471
+ paper: 'A4',
472
+ header: '{{company.name}}',
473
+ footer: 'Page {{page}} of {{pages}}',
474
+ blocks: [
475
+ { kind: 'heading', id: 'title', text: 'Certificate of withholding at source', level: 1 },
476
+ {
477
+ kind: 'fields',
478
+ id: 'payment',
479
+ items: [
480
+ { label: 'Payment', bind: 'name' },
481
+ { label: 'Date', bind: 'date' },
482
+ { label: 'Paid to', bind: 'partner.name' },
483
+ { label: 'Amount paid', bind: 'amount' },
484
+ { label: 'Withheld', bind: 'amountWithheld' },
485
+ ],
486
+ },
487
+ {
488
+ kind: 'table',
489
+ id: 'withheld',
490
+ bind: 'withholdings',
491
+ columns: [
492
+ { label: 'Withholding', bind: 'withholding.name' },
493
+ { label: 'Base', bind: 'base', align: 'end' },
494
+ { label: 'Amount', bind: 'amount', align: 'end', sum: true },
495
+ ],
496
+ },
497
+ { kind: 'text', id: 'in_words', text: 'Withheld: {{amountWithheld|words}}' },
498
+ ],
499
+ },
500
+ },
402
501
  {
403
502
  key: 'invoice',
404
503
  name: 'Invoice',
@@ -444,7 +543,7 @@ export const accounting = defineModule({
444
543
  { label: 'Quantity', bind: 'quantity', align: 'end' },
445
544
  { label: 'Unit price', bind: 'priceUnit', align: 'end' },
446
545
  { label: 'Discount %', bind: 'discount', align: 'end' },
447
- { label: 'Tax', bind: 'tax.name' },
546
+ { label: 'Tax', bind: 'taxAmount', align: 'end' },
448
547
  { label: 'Subtotal', bind: 'subtotal', align: 'end', sum: true },
449
548
  ],
450
549
  },
@@ -458,7 +557,9 @@ export const accounting = defineModule({
458
557
  { label: 'Total', bind: 'amountTotal' },
459
558
  ],
460
559
  },
560
+ { kind: 'text', id: 'in_words', text: '{{amountTotal|words}}' },
461
561
  { kind: 'text', id: 'terms', text: '{{paymentTerm.note}}' },
562
+ { kind: 'text', id: 'payment', text: '{{paymentDetails}}' },
462
563
  { kind: 'text', id: 'notes', text: '{{narration}}' },
463
564
  ],
464
565
  },
@@ -491,7 +592,7 @@ export const accounting = defineModule({
491
592
  ],
492
593
  },
493
594
  sheet: [
494
- k.group('party', ['partner', 'kind', 'invoiceOrigin', 'ref']),
595
+ k.group('party', ['partner', 'kind', 'invoiceOrigin', 'ref', 'debitOf', 'placeOfSupply']),
495
596
  k.group('dates', ['date', 'dateDue', 'paymentTerm', 'journal']),
496
597
  k.lines('lines', [
497
598
  'product',
@@ -500,7 +601,7 @@ export const accounting = defineModule({
500
601
  'quantity',
501
602
  'priceUnit',
502
603
  'discount',
503
- 'tax',
604
+ 'taxes',
504
605
  'subtotal',
505
606
  ], { readonlyWhen: { state: { ne: 'draft' } } }),
506
607
  k.group('totals', [
@@ -570,11 +671,11 @@ export const accounting = defineModule({
570
671
  measures: [{ sum: 'balance' }],
571
672
  comparison: { field: 'date', mode: 'yoy' },
572
673
  }),
674
+ // The tax lines of posted documents: each with the base its tax was computed on (SD in the VAT's base).
573
675
  'account.move.line.vat': pivot(AccountMoveLine, {
574
676
  rows: ['taxReport'],
575
- cols: ['lineKind'],
576
677
  measures: [
577
- { sum: 'subtotal', label: 'Base' },
678
+ { sum: 'taxBase', label: 'Base' },
578
679
  { sum: 'balance', label: 'Amount' },
579
680
  ],
580
681
  }),
@@ -601,8 +702,15 @@ export const accounting = defineModule({
601
702
  },
602
703
  sheet: [
603
704
  k.group('who', ['partner', 'kind', 'journal', 'date']),
604
- k.group('how_much', ['currency', 'amount', 'memo', 'move']),
705
+ k.group('how_much', ['currency', 'amount', 'amountWithheld', 'memo', 'move']),
605
706
  k.group('what', ['documents'], { label: 'Pays', columns: 1 }),
707
+ k.lines('withholdings', [
708
+ { field: 'withholding', id: 'withheld_by' },
709
+ { field: 'base', id: 'withheld_base' },
710
+ { field: 'amount', id: 'withheld_amount' },
711
+ ], {
712
+ readonlyWhen: { state: { ne: 'draft' } },
713
+ }),
606
714
  ],
607
715
  readonlyWhen: { state: { ne: 'draft' } },
608
716
  chatter: true,
@@ -681,6 +789,24 @@ export const accounting = defineModule({
681
789
  header: { buttons: [] },
682
790
  sheet: [k.group('main', ['name', 'dateFrom', 'dateTo', 'state', 'lockDate'])],
683
791
  })),
792
+ 'account.withholding.list': list(AccountWithholding, {
793
+ columns: ['name', 'direction', 'base', 'rate', 'threshold', 'account'],
794
+ }),
795
+ 'account.withholding.form': form(AccountWithholding, (k) => ({
796
+ sheet: [
797
+ k.group('withholding', ['name', 'direction', 'sequence']),
798
+ k.group('amount', ['base', 'rate', 'threshold', 'account']),
799
+ ],
800
+ })),
801
+ 'res.bank.account.list': list(ResBankAccount, {
802
+ columns: ['bank', 'branch', 'number', 'routing', 'currency'],
803
+ }),
804
+ 'res.bank.account.form': form(ResBankAccount, (k) => ({
805
+ sheet: [
806
+ k.group('bank_details', ['bank', 'branch', 'routing', 'swift']),
807
+ k.group('account_details', ['number', 'holder', 'currency', 'sequence']),
808
+ ],
809
+ })),
684
810
  'res.currency.rate.list': list(ResCurrencyRate, {
685
811
  columns: ['date', 'currency', 'rate', 'company'],
686
812
  defaultOrder: ['date desc'],
@@ -700,7 +826,11 @@ export const accounting = defineModule({
700
826
  entries: { label: 'Journal entries', domain: { kind: 'entry' }, group: 'kind' },
701
827
  invoices: {
702
828
  label: 'Invoices and bills',
703
- domain: { kind: { in: ['out_invoice', 'out_refund', 'in_invoice', 'in_refund'] } },
829
+ domain: {
830
+ kind: {
831
+ in: ['out_invoice', 'out_refund', 'out_debit', 'in_invoice', 'in_refund', 'in_debit'],
832
+ },
833
+ },
704
834
  group: 'kind',
705
835
  },
706
836
  },
@@ -760,13 +890,23 @@ export const accounting = defineModule({
760
890
  demo: accountingDemo,
761
891
  },
762
892
  routes: (context) => {
893
+ // The charts: the generic one and the installed localizations' (RFC 0016).
894
+ const charts = chartsOf(context.registry.loaded.modules);
895
+ const localizations = localizationsOf(context.registry.loaded.modules);
763
896
  context.app.post('/api/v1/accounting/charts/install', async (request) => {
764
897
  const template = request.body?.template;
765
- if (typeof template !== 'string' || !TEMPLATES.includes(template))
766
- throw new HttpProblem(400, 'invalid-template', 'Unknown chart template', `template must be one of ${TEMPLATES.join(', ')}.`);
898
+ if (typeof template !== 'string' || !charts.has(template))
899
+ throw new HttpProblem(400, 'invalid-template', 'Unknown chart template', `template must be one of ${[...charts.keys()].join(', ')}.`);
767
900
  return request.env(async (env) => {
768
901
  try {
769
- return await installChart(env, template);
902
+ const localization = localizations.get(template);
903
+ if (localization === undefined)
904
+ return await installChart(env, template, charts.get(template));
905
+ // A country's chart comes with the rest of its localization (taxes, withholdings, fiscal year).
906
+ if ((await installedChart(env)) !== null)
907
+ throw new ChartInstalledError((await installedChart(env)));
908
+ const done = await installLocalization(env, localization);
909
+ return { ...done.chart, localization: done };
770
910
  }
771
911
  catch (error) {
772
912
  if (error instanceof ChartInstalledError)
@@ -777,8 +917,30 @@ export const accounting = defineModule({
777
917
  });
778
918
  context.app.get('/api/v1/accounting/charts', async (request) => request.env(async (env) => ({
779
919
  installed: await installedChart(env),
780
- templates: TEMPLATES.map((t) => ({ code: t, label: CHARTS[t].label })),
920
+ templates: [...charts].map(([code, chart]) => ({ code, label: chart.label })),
781
921
  })));
922
+ // What a payment of these documents should withhold (RFC 0016): the register-payment dialog shows it first.
923
+ const withholdingsOf = (input) => {
924
+ if (input === undefined)
925
+ return undefined;
926
+ if (!Array.isArray(input))
927
+ throw new HttpProblem(400, 'invalid-body', 'Invalid request', 'withholdings is a list.');
928
+ return input.map((w) => {
929
+ const row = (w ?? {});
930
+ const withholding = Number(row.withholding);
931
+ const amount = String(row.amount ?? '');
932
+ if (!Number.isInteger(withholding) || withholding <= 0 || !/^\d+(\.\d+)?$/.test(amount))
933
+ throw new HttpProblem(400, 'invalid-body', 'Invalid request', 'Each withholding is { withholding: id, amount: "decimal" }.');
934
+ return { withholding, amount };
935
+ });
936
+ };
937
+ context.app.post('/api/v1/accounting/payments/propose', async (request) => {
938
+ const moves = request.body?.moves;
939
+ const ids = Array.isArray(moves) ? moves.map(Number) : [];
940
+ if (ids.length === 0 || ids.some((id) => !Number.isInteger(id) || id <= 0))
941
+ throw new HttpProblem(400, 'invalid-body', 'Invalid request', 'moves is a list of document ids.');
942
+ return request.env((env) => proposeWithholdings(env, ids));
943
+ });
782
944
  context.app.post('/api/v1/accounting/payments/register', async (request) => {
783
945
  const body = (request.body ?? {});
784
946
  const moves = Array.isArray(body.moves) ? body.moves.map(Number) : [];
@@ -797,6 +959,7 @@ export const accounting = defineModule({
797
959
  date: body.date === undefined ? undefined : String(body.date),
798
960
  amount: body.amount === undefined ? undefined : String(body.amount),
799
961
  memo: body.memo === undefined ? undefined : String(body.memo),
962
+ withholdings: withholdingsOf(body.withholdings),
800
963
  }), { write: true });
801
964
  });
802
965
  const lineIds = (body) => {
@@ -855,6 +1018,18 @@ export const accounting = defineModule({
855
1018
  context.app.get('/api/v1/accounting/settings', async (request) => request.env((env) => readSettings(env)));
856
1019
  context.app.put('/api/v1/accounting/settings', async (request) => {
857
1020
  const body = (request.body ?? {});
1021
+ const code = (v, name, pattern, example) => {
1022
+ if (v === undefined)
1023
+ return undefined;
1024
+ if (v === null || v === '')
1025
+ return null;
1026
+ const value = String(v).toUpperCase();
1027
+ if (!pattern.test(value))
1028
+ throw new HttpProblem(400, 'invalid-body', 'Invalid request', `${name} is like ${example}.`);
1029
+ return value;
1030
+ };
1031
+ const country = code(body.country, 'country', /^[A-Z]{2}$/, 'IN');
1032
+ const state = code(body.state, 'state', /^[A-Z]{2}-[A-Z0-9]{1,3}$/, 'IN-MH');
858
1033
  const date = (v, name) => {
859
1034
  if (v === undefined)
860
1035
  return undefined;
@@ -869,6 +1044,8 @@ export const accounting = defineModule({
869
1044
  return request.env((env) => updateSettings(env, {
870
1045
  ...(lockDate === undefined ? {} : { lockDate }),
871
1046
  ...(taxLockDate === undefined ? {} : { taxLockDate }),
1047
+ ...(country === undefined ? {} : { country }),
1048
+ ...(state === undefined ? {} : { state }),
872
1049
  }), { write: true });
873
1050
  });
874
1051
  context.app.post('/api/v1/accounting/fiscal-years/:id/close', async (request) => {