shutro-erp 0.2.0 → 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.
- package/README.md +17 -10
- package/dist/account_portal/.kosh/bindings.d.ts +22 -0
- package/dist/account_portal/.kosh/bindings.js +15 -0
- package/dist/account_portal/.kosh/schema.d.ts +1350 -0
- package/dist/account_portal/index.d.ts +3 -0
- package/dist/account_portal/index.js +73 -0
- package/dist/account_portal/module.d.ts +5983 -0
- package/dist/account_portal/module.js +18 -0
- package/dist/accounting/.kosh/bindings.js +1 -1
- package/dist/accounting/.kosh/schema.d.ts +142 -9
- package/dist/accounting/chart.d.ts +40 -8
- package/dist/accounting/chart.js +124 -30
- package/dist/accounting/closing.d.ts +6 -1
- package/dist/accounting/closing.js +5 -1
- package/dist/accounting/demo.js +6 -5
- package/dist/accounting/index.d.ts +3 -3
- package/dist/accounting/index.js +200 -23
- package/dist/accounting/module.d.ts +3326 -1086
- package/dist/accounting/module.js +408 -66
- package/dist/accounting/payments.d.ts +25 -0
- package/dist/accounting/payments.js +96 -4
- package/dist/accounting/web/RegisterPayment.d.ts +3 -2
- package/dist/accounting/web/RegisterPayment.js +39 -6
- package/dist/accounting/web/SettingsPage.js +5 -1
- package/dist/accounting/web/messages.js +10 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/init.d.ts +5 -0
- package/dist/cli/init.js +195 -53
- package/dist/cli/launch.d.ts +16 -0
- package/dist/cli/launch.js +138 -0
- package/dist/cli/prompt.d.ts +35 -0
- package/dist/cli/prompt.js +46 -0
- package/dist/cli/templates.d.ts +11 -3
- package/dist/cli/templates.js +32 -7
- package/dist/cli/tui.d.ts +7 -0
- package/dist/cli/tui.js +254 -0
- package/dist/cli/ui.d.ts +25 -5
- package/dist/cli/ui.js +49 -9
- package/dist/contacts/.kosh/bindings.js +1 -1
- package/dist/contacts/.kosh/schema.d.ts +3 -1
- package/dist/contacts/index.js +24 -1
- package/dist/contacts/module.d.ts +42 -0
- package/dist/contacts/module.js +3 -1
- package/dist/import/convert.d.ts +55 -0
- package/dist/import/convert.js +224 -0
- package/dist/import/csv.d.ts +28 -0
- package/dist/import/csv.js +115 -0
- package/dist/import/index.d.ts +33 -0
- package/dist/import/index.js +60 -0
- package/dist/import/mapping.d.ts +15 -0
- package/dist/import/mapping.js +33 -0
- package/dist/import/match.d.ts +24 -0
- package/dist/import/match.js +60 -0
- package/dist/import/models.d.ts +2 -0
- package/dist/import/models.js +102 -0
- package/dist/import/routes.d.ts +13 -0
- package/dist/import/routes.js +392 -0
- package/dist/import/run.d.ts +131 -0
- package/dist/import/run.js +568 -0
- package/dist/import/service.d.ts +153 -0
- package/dist/import/service.js +367 -0
- package/dist/import/web/ImportButton.d.ts +7 -0
- package/dist/import/web/ImportButton.js +31 -0
- package/dist/import/web/ImportWizard.d.ts +75 -0
- package/dist/import/web/ImportWizard.js +217 -0
- package/dist/import/web/index.d.ts +13 -0
- package/dist/import/web/index.js +18 -0
- package/dist/import/web/messages.d.ts +2 -0
- package/dist/import/web/messages.js +139 -0
- package/dist/import/worker.d.ts +3 -0
- package/dist/import/worker.js +52 -0
- package/dist/import/xlsx.d.ts +20 -0
- package/dist/import/xlsx.js +192 -0
- package/dist/l10n_bd/.kosh/bindings.d.ts +22 -0
- package/dist/l10n_bd/.kosh/bindings.js +15 -0
- package/dist/l10n_bd/.kosh/schema.d.ts +1281 -0
- package/dist/l10n_bd/documents.d.ts +8 -0
- package/dist/l10n_bd/documents.js +183 -0
- package/dist/l10n_bd/index.d.ts +8 -0
- package/dist/l10n_bd/index.js +156 -0
- package/dist/l10n_bd/module.d.ts +5705 -0
- package/dist/l10n_bd/module.js +163 -0
- package/dist/l10n_bd/mushak.d.ts +67 -0
- package/dist/l10n_bd/mushak.js +139 -0
- package/dist/l10n_bd/routes.d.ts +3 -0
- package/dist/l10n_bd/routes.js +205 -0
- package/dist/l10n_bd/web/MushakPage.d.ts +7 -0
- package/dist/l10n_bd/web/MushakPage.js +53 -0
- package/dist/l10n_bd/web/index.d.ts +13 -0
- package/dist/l10n_bd/web/index.js +18 -0
- package/dist/l10n_bd/web/messages.d.ts +2 -0
- package/dist/l10n_bd/web/messages.js +101 -0
- package/dist/l10n_in/.kosh/bindings.d.ts +22 -0
- package/dist/l10n_in/.kosh/bindings.js +15 -0
- package/dist/l10n_in/.kosh/schema.d.ts +1343 -0
- package/dist/l10n_in/documents.d.ts +8 -0
- package/dist/l10n_in/documents.js +114 -0
- package/dist/l10n_in/einvoice.d.ts +253 -0
- package/dist/l10n_in/einvoice.js +485 -0
- package/dist/l10n_in/gstr.d.ts +109 -0
- package/dist/l10n_in/gstr.js +430 -0
- package/dist/l10n_in/index.d.ts +20 -0
- package/dist/l10n_in/index.js +232 -0
- package/dist/l10n_in/module.d.ts +7823 -0
- package/dist/l10n_in/module.js +427 -0
- package/dist/l10n_in/routes-einvoice.d.ts +15 -0
- package/dist/l10n_in/routes-einvoice.js +124 -0
- package/dist/l10n_in/routes.d.ts +17 -0
- package/dist/l10n_in/routes.js +282 -0
- package/dist/l10n_in/web/EInvoicePanel.d.ts +8 -0
- package/dist/l10n_in/web/EInvoicePanel.js +63 -0
- package/dist/l10n_in/web/GstPage.d.ts +7 -0
- package/dist/l10n_in/web/GstPage.js +57 -0
- package/dist/l10n_in/web/GstSettings.d.ts +10 -0
- package/dist/l10n_in/web/GstSettings.js +36 -0
- package/dist/l10n_in/web/index.d.ts +15 -0
- package/dist/l10n_in/web/index.js +22 -0
- package/dist/l10n_in/web/messages.d.ts +2 -0
- package/dist/l10n_in/web/messages.js +166 -0
- package/dist/modules-sdk/demo.d.ts +2 -0
- package/dist/modules-sdk/demo.js +7 -3
- package/dist/modules-sdk/index.d.ts +2 -2
- package/dist/modules-sdk/index.js +1 -1
- package/dist/modules-sdk/manifest.d.ts +132 -0
- package/dist/modules-sdk/manifest.js +22 -0
- package/dist/notify/inbox.js +21 -0
- package/dist/notify/messages.js +4 -0
- package/dist/notify/models.d.ts +1 -1
- package/dist/notify/models.js +2 -0
- package/dist/notify/web/messages.js +4 -0
- package/dist/portal/.kosh/bindings.d.ts +22 -0
- package/dist/portal/.kosh/bindings.js +15 -0
- package/dist/portal/.kosh/schema.d.ts +346 -0
- package/dist/portal/announce.d.ts +16 -0
- package/dist/portal/announce.js +117 -0
- package/dist/portal/documents.d.ts +17 -0
- package/dist/portal/documents.js +61 -0
- package/dist/portal/index.d.ts +20 -0
- package/dist/portal/index.js +65 -0
- package/dist/portal/module.d.ts +1405 -0
- package/dist/portal/module.js +84 -0
- package/dist/portal/routes-documents.d.ts +2 -0
- package/dist/portal/routes-documents.js +391 -0
- package/dist/portal/routes.d.ts +13 -0
- package/dist/portal/routes.js +313 -0
- package/dist/portal/rules.d.ts +11 -0
- package/dist/portal/rules.js +25 -0
- package/dist/portal/web/PortalAccess.d.ts +8 -0
- package/dist/portal/web/PortalAccess.js +66 -0
- package/dist/portal/web/index.d.ts +12 -0
- package/dist/portal/web/index.js +17 -0
- package/dist/portal/web/messages.d.ts +2 -0
- package/dist/portal/web/messages.js +33 -0
- package/dist/product/.kosh/bindings.js +1 -1
- package/dist/product/.kosh/schema.d.ts +12 -3
- package/dist/product/demo.js +6 -5
- package/dist/product/index.d.ts +1 -0
- package/dist/product/index.js +17 -4
- package/dist/product/module.d.ts +346 -14
- package/dist/product/module.js +48 -18
- package/dist/product/taxes.d.ts +59 -0
- package/dist/product/taxes.js +61 -0
- package/dist/project/.kosh/bindings.js +1 -1
- package/dist/project/.kosh/schema.d.ts +3 -1
- package/dist/project/module.d.ts +20 -0
- package/dist/project_portal/.kosh/bindings.d.ts +22 -0
- package/dist/project_portal/.kosh/bindings.js +15 -0
- package/dist/project_portal/.kosh/schema.d.ts +671 -0
- package/dist/project_portal/index.d.ts +9 -0
- package/dist/project_portal/index.js +42 -0
- package/dist/project_portal/module.d.ts +2142 -0
- package/dist/project_portal/module.js +22 -0
- package/dist/purchase/.kosh/bindings.js +1 -1
- package/dist/purchase/.kosh/schema.d.ts +15 -4
- package/dist/purchase/demo.js +3 -3
- package/dist/purchase/index.js +1 -1
- package/dist/purchase/module.d.ts +232 -8
- package/dist/purchase/module.js +51 -9
- package/dist/purchase_account/.kosh/bindings.js +1 -1
- package/dist/purchase_account/.kosh/schema.d.ts +144 -10
- package/dist/purchase_account/module.d.ts +2008 -641
- package/dist/purchase_account/module.js +2 -2
- package/dist/purchase_portal/.kosh/bindings.d.ts +22 -0
- package/dist/purchase_portal/.kosh/bindings.js +15 -0
- package/dist/purchase_portal/.kosh/schema.d.ts +822 -0
- package/dist/purchase_portal/index.d.ts +9 -0
- package/dist/purchase_portal/index.js +53 -0
- package/dist/purchase_portal/module.d.ts +2758 -0
- package/dist/purchase_portal/module.js +18 -0
- package/dist/reporting/documents.js +20 -3
- package/dist/reporting/index.d.ts +1 -0
- package/dist/reporting/index.js +1 -0
- package/dist/reporting/routes-documents.js +9 -2
- package/dist/reporting/words.d.ts +11 -0
- package/dist/reporting/words.js +229 -0
- package/dist/sale_account/.kosh/bindings.js +1 -1
- package/dist/sale_account/.kosh/schema.d.ts +144 -10
- package/dist/sale_account/module.d.ts +2008 -641
- package/dist/sale_account/module.js +3 -3
- package/dist/sale_portal/.kosh/bindings.d.ts +22 -0
- package/dist/sale_portal/.kosh/bindings.js +15 -0
- package/dist/sale_portal/.kosh/schema.d.ts +736 -0
- package/dist/sale_portal/index.d.ts +9 -0
- package/dist/sale_portal/index.js +71 -0
- package/dist/sale_portal/module.d.ts +2718 -0
- package/dist/sale_portal/module.js +19 -0
- package/dist/sale_project/.kosh/bindings.js +1 -1
- package/dist/sale_project/.kosh/schema.d.ts +15 -4
- package/dist/sale_project/module.d.ts +390 -10
- package/dist/sales/.kosh/bindings.js +1 -1
- package/dist/sales/.kosh/schema.d.ts +15 -4
- package/dist/sales/demo.js +3 -3
- package/dist/sales/index.js +67 -1
- package/dist/sales/module.d.ts +232 -8
- package/dist/sales/module.js +51 -12
- package/dist/server/auth/base.js +2 -0
- package/dist/server/auth/service.d.ts +2 -0
- package/dist/server/auth/service.js +7 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/server.js +26 -1
- package/dist/ui/api.d.ts +2 -0
- package/dist/ui/app.js +1 -1
- package/dist/ui/context.d.ts +3 -1
- package/dist/ui/context.js +2 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/lib/format.d.ts +2 -0
- package/dist/ui/lib/format.js +3 -1
- package/dist/ui/views/FormView.js +19 -6
- package/package.json +50 -2
package/dist/accounting/demo.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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, '
|
|
83
|
-
return 'the
|
|
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;
|
package/dist/accounting/index.js
CHANGED
|
@@ -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 {
|
|
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,
|
|
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
|
-
|
|
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: '
|
|
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
|
-
'
|
|
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: '
|
|
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: {
|
|
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' || !
|
|
766
|
-
throw new HttpProblem(400, 'invalid-template', 'Unknown chart template', `template must be one of ${
|
|
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
|
-
|
|
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:
|
|
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) => {
|