@things-factory/accounting 10.1.206 → 10.1.209
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/dist-server/activities/activity-expenditure.js +5 -2
- package/dist-server/activities/activity-expenditure.js.map +1 -1
- package/dist-server/service/account/account-match.d.ts +4 -5
- package/dist-server/service/account/account-match.js +7 -8
- package/dist-server/service/account/account-match.js.map +1 -1
- package/dist-server/service/account/account-mutation.js +5 -7
- package/dist-server/service/account/account-mutation.js.map +1 -1
- package/dist-server/service/account/account-service.d.ts +5 -0
- package/dist-server/service/account/account-service.js +17 -0
- package/dist-server/service/account/account-service.js.map +1 -0
- package/dist-server/service/bank-account/bank-account-service.d.ts +4 -1
- package/dist-server/service/bank-account/bank-account-service.js +5 -1
- package/dist-server/service/bank-account/bank-account-service.js.map +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-item.d.ts +4 -3
- package/dist-server/service/expenditure-request/expenditure-request-item.js +6 -5
- package/dist-server/service/expenditure-request/expenditure-request-item.js.map +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-mutation.d.ts +2 -2
- package/dist-server/service/expenditure-request/expenditure-request-mutation.js +13 -11
- package/dist-server/service/expenditure-request/expenditure-request-mutation.js.map +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-query.d.ts +9 -2
- package/dist-server/service/expenditure-request/expenditure-request-query.js +28 -6
- package/dist-server/service/expenditure-request/expenditure-request-query.js.map +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-service.d.ts +14 -4
- package/dist-server/service/expenditure-request/expenditure-request-service.js +47 -9
- package/dist-server/service/expenditure-request/expenditure-request-service.js.map +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-type.d.ts +1 -1
- package/dist-server/service/expenditure-request/expenditure-request-type.js +3 -3
- package/dist-server/service/expenditure-request/expenditure-request-type.js.map +1 -1
- package/dist-server/service/expenditure-request/receipt-reader.d.ts +18 -9
- package/dist-server/service/expenditure-request/receipt-reader.js +19 -10
- package/dist-server/service/expenditure-request/receipt-reader.js.map +1 -1
- package/dist-server/service/index.d.ts +1 -0
- package/dist-server/service/index.js +1 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/payment/index.js +3 -0
- package/dist-server/service/payment/index.js.map +1 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.d.ts +0 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.js +0 -17
- package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.js.map +1 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item-service.d.ts +4 -8
- package/dist-server/service/recurring-cash-item/recurring-cash-item-service.js +5 -38
- package/dist-server/service/recurring-cash-item/recurring-cash-item-service.js.map +1 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item-type.js +1 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item-type.js.map +1 -1
- package/dist-server/service/recurring-cash-item/recurring-cash-item.d.ts +2 -2
- package/dist-server/service/recurring-cash-item/recurring-cash-item.js +5 -5
- package/dist-server/service/recurring-cash-item/recurring-cash-item.js.map +1 -1
- package/dist-server/service/recurring-cash-item/recurring-schedule.d.ts +6 -18
- package/dist-server/service/recurring-cash-item/recurring-schedule.js +13 -26
- package/dist-server/service/recurring-cash-item/recurring-schedule.js.map +1 -1
- package/dist-server/service/transaction/transaction-history.d.ts +1 -7
- package/dist-server/service/transaction/transaction-history.js +0 -25
- package/dist-server/service/transaction/transaction-history.js.map +1 -1
- package/dist-server/service/transaction/transaction-period.d.ts +0 -8
- package/dist-server/service/transaction/transaction-period.js +0 -30
- package/dist-server/service/transaction/transaction-period.js.map +1 -1
- package/dist-server/service/transaction/transaction-query.d.ts +0 -2
- package/dist-server/service/transaction/transaction-query.js +2 -16
- package/dist-server/service/transaction/transaction-query.js.map +1 -1
- package/dist-server/service/transaction/transaction-service.d.ts +4 -6
- package/dist-server/service/transaction/transaction-service.js +10 -35
- package/dist-server/service/transaction/transaction-service.js.map +1 -1
- package/dist-server/service/transaction/transaction-type.d.ts +1 -15
- package/dist-server/service/transaction/transaction-type.js +2 -58
- package/dist-server/service/transaction/transaction-type.js.map +1 -1
- package/dist-server/service/transaction/transaction.d.ts +6 -15
- package/dist-server/service/transaction/transaction.js +13 -52
- package/dist-server/service/transaction/transaction.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/translations/en.json +12 -6
- package/translations/es.json +12 -6
- package/translations/ja.json +11 -1
- package/translations/ko.json +13 -7
- package/translations/ms.json +11 -1
- package/translations/zh.json +11 -1
|
@@ -41,7 +41,8 @@ const MODEL = [
|
|
|
41
41
|
{ name: 'requester', description: 'Who requests', tag: 'requester', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },
|
|
42
42
|
{ name: 'payee', description: 'Who is paid', tag: 'payee', hidden: true, mandatory: false, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },
|
|
43
43
|
{ name: 'paidTo', description: 'Who is paid, as a reference its 360 opens from', tag: 'paidTo', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} },
|
|
44
|
-
{ name: 'lines', description: 'The lines as the approver reads them', tag: 'lines', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} }
|
|
44
|
+
{ name: 'lines', description: 'The lines as the approver reads them', tag: 'lines', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} },
|
|
45
|
+
{ name: 'request', description: 'The request, as a reference its lines and files open from', tag: 'request', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} }
|
|
45
46
|
];
|
|
46
47
|
/*
|
|
47
48
|
* What the 업무 카탈로그 shows of how far these forms are (worklist `ActivityReadiness`). `lastRun` is written by
|
|
@@ -51,7 +52,7 @@ const MODEL = [
|
|
|
51
52
|
const READINESS = {
|
|
52
53
|
stage: 'working',
|
|
53
54
|
tests: ['packages/accounting/test/expenditure-requests.test.mjs'],
|
|
54
|
-
lastRun: { passed:
|
|
55
|
+
lastRun: { passed: 21, total: 21, at: '2026-10-23T15:39:22+09:00', command: 'npm run build:server && node --test test/expenditure-requests.test.mjs (packages/accounting)' }
|
|
55
56
|
};
|
|
56
57
|
const COMMON = {
|
|
57
58
|
release: '1.0.0',
|
|
@@ -91,6 +92,7 @@ exports.ActivityExpenditureRequest = {
|
|
|
91
92
|
{ role: 'headline', inputPath: 'headline', name: 'what is to be paid' },
|
|
92
93
|
{ role: 'subject', inputPath: 'paidTo', tKey: 'field.payee', name: 'who is paid' },
|
|
93
94
|
{ role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what it pays for' },
|
|
95
|
+
{ role: 'evidence', inputPath: 'request', tKey: 'field.expenditure-files', name: 'the receipts and papers of the lines' },
|
|
94
96
|
{ role: 'consequence', inputPath: 'consequence', name: 'what approving does' }
|
|
95
97
|
]
|
|
96
98
|
};
|
|
@@ -115,6 +117,7 @@ exports.ActivityExpenseClaim = {
|
|
|
115
117
|
{ role: 'headline', inputPath: 'headline', name: 'what is claimed' },
|
|
116
118
|
{ role: 'subject', inputPath: 'paidTo', tKey: 'field.requester', name: 'who claims' },
|
|
117
119
|
{ role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what was spent, on what' },
|
|
120
|
+
{ role: 'evidence', inputPath: 'request', tKey: 'field.expenditure-files', name: 'the receipts and papers of the lines' },
|
|
118
121
|
{ role: 'consequence', inputPath: 'consequence', name: 'what approving does' }
|
|
119
122
|
]
|
|
120
123
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-expenditure.js","sourceRoot":"","sources":["../../server/activities/activity-expenditure.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActivityInstance, UpdateActivityInstanceStateAddendum } from '@things-factory/worklist'\nimport { speaksRefusals } from '@things-factory/shell'\n\nimport { ExpenditureRequest, ExpenditureRequestStatus } from '../service/expenditure-request/expenditure-request'\nimport { decisionOf, type RequestStatus } from '../service/expenditure-request/expenditure-request-rules'\nimport { EXPENDITURE_REQUEST_ACTIVITY, EXPENSE_CLAIM_ACTIVITY } from './expenditure-activity-names'\n\n/**\n * What an approver's decision does to the request.\n *\n * worklist calls this on every state change of the instance, in the same transaction, and more than once for one\n * change. `decisionOf` lets a decision land only on the request still waiting on this instance - so a repeat call, or\n * a call about an instance the requester already withdrew and replaced, changes nothing.\n *\n * A rejection does not end the instance; the rejected thread in `causedBy` is how it is known. The person deciding is\n * `context.state.user` - the approver, not the requester.\n */\nexport async function decideExpenditure(activityInstance: ActivityInstance, addendum: UpdateActivityInstanceStateAddendum, context: ResolverContext) {\n const { domain, user, tx } = context.state\n const requestId = (activityInstance.input as any)?.requestId\n if (!requestId) return\n\n const repo = tx.getRepository(ExpenditureRequest)\n const request = await repo.findOne({ where: { id: requestId, domain: { id: domain.id } } as any })\n if (!request) return\n\n const decision = decisionOf(\n { id: activityInstance.id, state: String(activityInstance.state) },\n (addendum?.causedBy ?? []).map(thread => ({ state: String(thread.state) })),\n { status: request.status as RequestStatus, activityInstanceId: request.activityInstanceId }\n )\n if (!decision) return\n\n request.status = ExpenditureRequestStatus[decision]\n if (decision !== 'WITHDRAWN') {\n request.decidedAt = new Date()\n request.decidedBy = user\n }\n await repo.save(request)\n}\n\nconst MODEL = [\n { name: 'requestId', description: 'The request this approval decides', tag: 'requestId', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'month', description: 'Month, at the top level so the search key can index it', tag: 'month', hidden: true, mandatory: false, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'requester', description: 'Who requests', tag: 'requester', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'payee', description: 'Who is paid', tag: 'payee', hidden: true, mandatory: false, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'paidTo', description: 'Who is paid, as a reference its 360 opens from', tag: 'paidTo', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'lines', description: 'The lines as the approver reads them', tag: 'lines', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} }\n]\n\n/*\n * What the 업무 카탈로그 shows of how far these forms are (worklist `ActivityReadiness`). `lastRun` is written by\n * hand: change the tests, and change this line with them - the screen shows the day it was measured, so a stale\n * count reads as stale.\n */\nconst READINESS = {\n stage: 'working',\n tests: ['packages/accounting/test/expenditure-requests.test.mjs'],\n lastRun: { passed: 18, total: 18, at: '2026-10-16T21:09:48+09:00', command: 'npm run build:server && node --test test/expenditure-requests.test.mjs (packages/accounting)' }\n}\n\nconst COMMON = {\n release: '1.0.0',\n provider: 'hatiolab.com',\n category: 'accounting',\n activityType: 'user',\n priority: 1,\n startingType: 'post',\n multiple: 'single',\n /* No screen of its own: the generic approval screen draws the decision keys. Set explicitly - leaving it out keeps an old value. */\n viewType: '',\n viewSource: '',\n model: MODEL,\n standardTime: 3 * 24 * 60 * 60,\n readiness: READINESS,\n /* Runs inside worklist's mutation, where no resolver's `SpeakRefusals` reaches. */\n callback: speaksRefusals(decideExpenditure)\n}\n\n/** 지출결의 - paying a counterparty. */\nexport const ActivityExpenditureRequest = {\n ...COMMON,\n name: EXPENDITURE_REQUEST_ACTIVITY,\n label: 'expenditure request',\n description: 'Approval of a payment to a counterparty before the money leaves',\n guide: [\n 'someone asks the company to pay a counterparty - an invoice, a service, a deposit - into an account of that counterparty.',\n 'approving puts it in the payment queue; finance pays it, and the payment is reconciled with the bank statement.',\n 'the account approvers see is the account paid; if it changes after the approval, finance is told before paying.',\n 'rejecting returns the request to its writer, who can correct it and submit again.',\n 'with no approver named for this domain a request cannot be submitted.'\n ],\n searchKeys: [\n { name: 'payee', description: 'Who is paid', inputKey: 'payee', tKey: 'field.payee' },\n { name: 'requester', description: 'Who requests', inputKey: 'requester', tKey: 'field.requester' }\n ],\n decisionKeys: [\n { role: 'headline', inputPath: 'headline', name: 'what is to be paid' },\n { role: 'subject', inputPath: 'paidTo', tKey: 'field.payee', name: 'who is paid' },\n { role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what it pays for' },\n { role: 'consequence', inputPath: 'consequence', name: 'what approving does' }\n ]\n}\n\n/** 경비 청구 - paying an employee back what they spent for the company in a month. */\nexport const ActivityExpenseClaim = {\n ...COMMON,\n name: EXPENSE_CLAIM_ACTIVITY,\n label: 'expense claim',\n description: 'Approval of what an employee spent for the company in a month',\n guide: [\n 'an employee claims what they spent for the company - meals, travel, supplies.',\n 'approving puts it in the payment queue; finance pays the employee back, and the payment is reconciled with the bank statement.',\n 'lines paid with the corporate card are reviewed the same way but nothing is paid back for them.',\n 'rejecting returns the claim to the employee, who can correct it and submit again.',\n 'with no approver named for this domain a claim cannot be submitted.'\n ],\n searchKeys: [\n { name: 'month', description: 'Which month', inputKey: 'month', tKey: 'field.month' },\n { name: 'requester', description: 'Who claims', inputKey: 'requester', tKey: 'field.requester' }\n ],\n decisionKeys: [\n { role: 'headline', inputPath: 'headline', name: 'what is claimed' },\n { role: 'subject', inputPath: 'paidTo', tKey: 'field.requester', name: 'who claims' },\n { role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what was spent, on what' },\n { role: 'consequence', inputPath: 'consequence', name: 'what approving does' }\n ]\n}\n\n/** The approvals of an expenditure request - an application that uses them installs these into its domains. */\nexport const EXPENDITURE_ACTIVITIES = [ActivityExpenditureRequest, ActivityExpenseClaim]\n"]}
|
|
1
|
+
{"version":3,"file":"activity-expenditure.js","sourceRoot":"","sources":["../../server/activities/activity-expenditure.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActivityInstance, UpdateActivityInstanceStateAddendum } from '@things-factory/worklist'\nimport { speaksRefusals } from '@things-factory/shell'\n\nimport { ExpenditureRequest, ExpenditureRequestStatus } from '../service/expenditure-request/expenditure-request'\nimport { decisionOf, type RequestStatus } from '../service/expenditure-request/expenditure-request-rules'\nimport { EXPENDITURE_REQUEST_ACTIVITY, EXPENSE_CLAIM_ACTIVITY } from './expenditure-activity-names'\n\n/**\n * What an approver's decision does to the request.\n *\n * worklist calls this on every state change of the instance, in the same transaction, and more than once for one\n * change. `decisionOf` lets a decision land only on the request still waiting on this instance - so a repeat call, or\n * a call about an instance the requester already withdrew and replaced, changes nothing.\n *\n * A rejection does not end the instance; the rejected thread in `causedBy` is how it is known. The person deciding is\n * `context.state.user` - the approver, not the requester.\n */\nexport async function decideExpenditure(activityInstance: ActivityInstance, addendum: UpdateActivityInstanceStateAddendum, context: ResolverContext) {\n const { domain, user, tx } = context.state\n const requestId = (activityInstance.input as any)?.requestId\n if (!requestId) return\n\n const repo = tx.getRepository(ExpenditureRequest)\n const request = await repo.findOne({ where: { id: requestId, domain: { id: domain.id } } as any })\n if (!request) return\n\n const decision = decisionOf(\n { id: activityInstance.id, state: String(activityInstance.state) },\n (addendum?.causedBy ?? []).map(thread => ({ state: String(thread.state) })),\n { status: request.status as RequestStatus, activityInstanceId: request.activityInstanceId }\n )\n if (!decision) return\n\n request.status = ExpenditureRequestStatus[decision]\n if (decision !== 'WITHDRAWN') {\n request.decidedAt = new Date()\n request.decidedBy = user\n }\n await repo.save(request)\n}\n\nconst MODEL = [\n { name: 'requestId', description: 'The request this approval decides', tag: 'requestId', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'month', description: 'Month, at the top level so the search key can index it', tag: 'month', hidden: true, mandatory: false, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'requester', description: 'Who requests', tag: 'requester', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'payee', description: 'Who is paid', tag: 'payee', hidden: true, mandatory: false, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'paidTo', description: 'Who is paid, as a reference its 360 opens from', tag: 'paidTo', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'lines', description: 'The lines as the approver reads them', tag: 'lines', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'request', description: 'The request, as a reference its lines and files open from', tag: 'request', hidden: true, mandatory: false, inout: 'in', type: 'object', options: {}, unit: null, quantifier: [1], spec: {} }\n]\n\n/*\n * What the 업무 카탈로그 shows of how far these forms are (worklist `ActivityReadiness`). `lastRun` is written by\n * hand: change the tests, and change this line with them - the screen shows the day it was measured, so a stale\n * count reads as stale.\n */\nconst READINESS = {\n stage: 'working',\n tests: ['packages/accounting/test/expenditure-requests.test.mjs'],\n lastRun: { passed: 21, total: 21, at: '2026-10-23T15:39:22+09:00', command: 'npm run build:server && node --test test/expenditure-requests.test.mjs (packages/accounting)' }\n}\n\nconst COMMON = {\n release: '1.0.0',\n provider: 'hatiolab.com',\n category: 'accounting',\n activityType: 'user',\n priority: 1,\n startingType: 'post',\n multiple: 'single',\n /* No screen of its own: the generic approval screen draws the decision keys. Set explicitly - leaving it out keeps an old value. */\n viewType: '',\n viewSource: '',\n model: MODEL,\n standardTime: 3 * 24 * 60 * 60,\n readiness: READINESS,\n /* Runs inside worklist's mutation, where no resolver's `SpeakRefusals` reaches. */\n callback: speaksRefusals(decideExpenditure)\n}\n\n/** 지출결의 - paying a counterparty. */\nexport const ActivityExpenditureRequest = {\n ...COMMON,\n name: EXPENDITURE_REQUEST_ACTIVITY,\n label: 'expenditure request',\n description: 'Approval of a payment to a counterparty before the money leaves',\n guide: [\n 'someone asks the company to pay a counterparty - an invoice, a service, a deposit - into an account of that counterparty.',\n 'approving puts it in the payment queue; finance pays it, and the payment is reconciled with the bank statement.',\n 'the account approvers see is the account paid; if it changes after the approval, finance is told before paying.',\n 'rejecting returns the request to its writer, who can correct it and submit again.',\n 'with no approver named for this domain a request cannot be submitted.'\n ],\n searchKeys: [\n { name: 'payee', description: 'Who is paid', inputKey: 'payee', tKey: 'field.payee' },\n { name: 'requester', description: 'Who requests', inputKey: 'requester', tKey: 'field.requester' }\n ],\n decisionKeys: [\n { role: 'headline', inputPath: 'headline', name: 'what is to be paid' },\n { role: 'subject', inputPath: 'paidTo', tKey: 'field.payee', name: 'who is paid' },\n { role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what it pays for' },\n { role: 'evidence', inputPath: 'request', tKey: 'field.expenditure-files', name: 'the receipts and papers of the lines' },\n { role: 'consequence', inputPath: 'consequence', name: 'what approving does' }\n ]\n}\n\n/** 경비 청구 - paying an employee back what they spent for the company in a month. */\nexport const ActivityExpenseClaim = {\n ...COMMON,\n name: EXPENSE_CLAIM_ACTIVITY,\n label: 'expense claim',\n description: 'Approval of what an employee spent for the company in a month',\n guide: [\n 'an employee claims what they spent for the company - meals, travel, supplies.',\n 'approving puts it in the payment queue; finance pays the employee back, and the payment is reconciled with the bank statement.',\n 'lines paid with the corporate card are reviewed the same way but nothing is paid back for them.',\n 'rejecting returns the claim to the employee, who can correct it and submit again.',\n 'with no approver named for this domain a claim cannot be submitted.'\n ],\n searchKeys: [\n { name: 'month', description: 'Which month', inputKey: 'month', tKey: 'field.month' },\n { name: 'requester', description: 'Who claims', inputKey: 'requester', tKey: 'field.requester' }\n ],\n decisionKeys: [\n { role: 'headline', inputPath: 'headline', name: 'what is claimed' },\n { role: 'subject', inputPath: 'paidTo', tKey: 'field.requester', name: 'who claims' },\n { role: 'reason', inputPath: 'lines', tKey: 'field.expenditure-lines', name: 'what was spent, on what' },\n { role: 'evidence', inputPath: 'request', tKey: 'field.expenditure-files', name: 'the receipts and papers of the lines' },\n { role: 'consequence', inputPath: 'consequence', name: 'what approving does' }\n ]\n}\n\n/** The approvals of an expenditure request - an application that uses them installs these into its domains. */\nexport const EXPENDITURE_ACTIVITIES = [ActivityExpenditureRequest, ActivityExpenseClaim]\n"]}
|
|
@@ -13,8 +13,8 @@ export declare function aliasesOf(account: Pick<AccountForMatch, 'name' | 'alias
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function accountMatcher<T extends AccountForMatch>(accounts: readonly T[]): (texts: readonly (string | null | undefined)[], type?: string | null) => T | undefined;
|
|
15
15
|
/**
|
|
16
|
-
* Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a
|
|
17
|
-
* its description then its ledger category,
|
|
16
|
+
* Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a bank line by
|
|
17
|
+
* its description then its ledger category, among the active accounts of its direction
|
|
18
18
|
* - the row's own, or the stored one for a change that does not say. A row that says `account` (an account or none)
|
|
19
19
|
* keeps what it says; a row whose words mean nothing is saved without one.
|
|
20
20
|
*/
|
|
@@ -22,10 +22,9 @@ export declare function assignAccounts<T extends Record<string, any>>(tx: Entity
|
|
|
22
22
|
id: string;
|
|
23
23
|
}, inputs: T[]): Promise<T[]>;
|
|
24
24
|
export interface AccountLinking {
|
|
25
|
-
/** Rows given an account, per kind:
|
|
25
|
+
/** Rows given an account, per kind: bank lines, recurring items, expenditure request lines. */
|
|
26
26
|
linked: {
|
|
27
27
|
transactions: number;
|
|
28
|
-
forecasts: number;
|
|
29
28
|
recurring: number;
|
|
30
29
|
expenditureLines: number;
|
|
31
30
|
};
|
|
@@ -34,7 +33,7 @@ export interface AccountLinking {
|
|
|
34
33
|
}
|
|
35
34
|
/**
|
|
36
35
|
* Give every row without an account the one its words mean: a confirmed transaction by its description then its
|
|
37
|
-
* ledger category, a
|
|
36
|
+
* ledger category, a recurring item by its name, an expenditure request line by its
|
|
38
37
|
* category then its description, among the expense accounts. `apply` false writes nothing. Rows a person already filed
|
|
39
38
|
* are left alone.
|
|
40
39
|
*/
|
|
@@ -45,36 +45,36 @@ function accountMatcher(accounts) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a
|
|
49
|
-
* its description then its ledger category,
|
|
48
|
+
* Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a bank line by
|
|
49
|
+
* its description then its ledger category, among the active accounts of its direction
|
|
50
50
|
* - the row's own, or the stored one for a change that does not say. A row that says `account` (an account or none)
|
|
51
51
|
* keeps what it says; a row whose words mean nothing is saved without one.
|
|
52
52
|
*/
|
|
53
53
|
async function assignAccounts(tx, domain, inputs) {
|
|
54
|
-
const textsOf = (row
|
|
54
|
+
const textsOf = (row) => [row.description, row.ledgerCategory];
|
|
55
55
|
const wants = (row) => row.cuFlag !== '-' && !('account' in row) && (row.description || row.ledgerCategory);
|
|
56
56
|
if (!inputs.some(wants))
|
|
57
57
|
return inputs;
|
|
58
58
|
const accounts = await tx.getRepository(account_1.Account).find({ where: { domain: { id: domain.id }, active: true } });
|
|
59
59
|
const match = accountMatcher(accounts);
|
|
60
60
|
const edited = inputs.filter(row => wants(row) && row.id && row.cuFlag !== '+').map(row => row.id);
|
|
61
|
-
const stored = new Map((edited.length ? await tx.getRepository(transaction_1.Transaction).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(edited) }, select: ['id', 'type'
|
|
61
|
+
const stored = new Map((edited.length ? await tx.getRepository(transaction_1.Transaction).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(edited) }, select: ['id', 'type'] }) : []).map(row => [row.id, row]));
|
|
62
62
|
return inputs.map(row => {
|
|
63
63
|
if (!wants(row))
|
|
64
64
|
return row;
|
|
65
65
|
const before = stored.get(row.id);
|
|
66
|
-
const account = match(textsOf(row
|
|
66
|
+
const account = match(textsOf(row), row.type ?? before?.type);
|
|
67
67
|
return account ? { ...row, account: { id: account.id } } : row;
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Give every row without an account the one its words mean: a confirmed transaction by its description then its
|
|
72
|
-
* ledger category, a
|
|
72
|
+
* ledger category, a recurring item by its name, an expenditure request line by its
|
|
73
73
|
* category then its description, among the expense accounts. `apply` false writes nothing. Rows a person already filed
|
|
74
74
|
* are left alone.
|
|
75
75
|
*/
|
|
76
76
|
async function linkAccounts(tx, domain, match, apply) {
|
|
77
|
-
const report = { linked: { transactions: 0,
|
|
77
|
+
const report = { linked: { transactions: 0, recurring: 0, expenditureLines: 0 }, unmatched: [] };
|
|
78
78
|
const run = async (label, entity, texts, typeOf, scope = {}) => {
|
|
79
79
|
const rows = (await tx.getRepository(entity).find({ where: { domain: { id: domain.id }, ...scope, account: (0, typeorm_1.IsNull)() } }));
|
|
80
80
|
const groups = new Map();
|
|
@@ -95,7 +95,6 @@ async function linkAccounts(tx, domain, match, apply) {
|
|
|
95
95
|
await tx.getRepository(entity).update(ids.slice(i, i + 500), { account: { id } });
|
|
96
96
|
};
|
|
97
97
|
await run('transactions', transaction_1.Transaction, row => [row.description, row.ledgerCategory], row => row.type, { category: transaction_1.TransactionCategory.CONFIRMED });
|
|
98
|
-
await run('forecasts', transaction_1.Transaction, row => [row.description], row => row.type, { category: transaction_1.TransactionCategory.FORECAST });
|
|
99
98
|
await run('recurring', recurring_cash_item_1.RecurringCashItem, row => [row.name], row => row.type);
|
|
100
99
|
await run('expenditureLines', expenditure_request_item_1.ExpenditureRequestItem, row => [row.category, row.description], () => cash_flow_type_1.CashFlowType.EXPENSE);
|
|
101
100
|
return report;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-match.js","sourceRoot":"","sources":["../../../server/service/account/account-match.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In, IsNull } from 'typeorm'\n\nimport { Account } from './account'\n\nimport { CashFlowType } from '../cash-flow-type'\nimport { ExpenditureRequestItem } from '../expenditure-request/expenditure-request-item'\nimport { RecurringCashItem } from '../recurring-cash-item/recurring-cash-item'\nimport { Transaction, TransactionCategory } from '../transaction/transaction'\n\n/*\n * Which account some words mean. An account is known by its name and its aliases; the longest one contained in the\n * text wins, among accounts of the row's direction. Two different accounts matching equally long is no answer, and\n * nothing matching is no answer - a row nobody can place stays unplaced, to be listed, not filed under a catch-all.\n */\n\nexport interface AccountForMatch {\n id: string\n name: string\n type: string\n aliases?: string | null\n}\n\nconst key = (text: string | null | undefined) =>\n String(text ?? '')\n .normalize('NFC')\n .replace(/[\\s\\-_.·,()]+/g, '')\n .toLowerCase()\n\n/** An account's name and its aliases (one per line, or comma separated). */\nexport function aliasesOf(account: Pick<AccountForMatch, 'name' | 'aliases'>): string[] {\n return [account.name, ...String(account.aliases ?? '').split(/[\\n,]/)].map(one => one.trim()).filter(Boolean)\n}\n\n/**\n * A function telling which account some words mean. `type` limits it to that direction - an expense is never filed\n * under an income account because a word happens to match.\n */\nexport function accountMatcher<T extends AccountForMatch>(accounts: readonly T[]) {\n const words: { word: string; account: T }[] = []\n for (const account of accounts) for (const alias of aliasesOf(account)) if (key(alias).length >= 2) words.push({ word: key(alias), account })\n words.sort((a, b) => b.word.length - a.word.length)\n\n return (texts: readonly (string | null | undefined)[], type?: string | null): T | undefined => {\n for (const text of texts) {\n const hay = key(text)\n if (!hay) continue\n const hits = words.filter(one => (!type || one.account.type === type) && hay.includes(one.word))\n if (!hits.length) continue\n const longest = hits[0].word.length\n const top = new Set(hits.filter(one => one.word.length === longest).map(one => one.account.id))\n return top.size === 1 ? hits[0].account : undefined\n }\n return undefined\n }\n}\n\n/**\n * Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a confirmed line by\n * its description then its ledger category, a forecast by its description, among the active accounts of its direction\n * - the row's own, or the stored one for a change that does not say. A row that says `account` (an account or none)\n * keeps what it says; a row whose words mean nothing is saved without one.\n */\nexport async function assignAccounts<T extends Record<string, any>>(tx: EntityManager, domain: { id: string }, inputs: T[]): Promise<T[]> {\n const textsOf = (row: Record<string, any>, category?: string) =>\n category === TransactionCategory.FORECAST ? [row.description] : [row.description, row.ledgerCategory]\n const wants = (row: Record<string, any>) => row.cuFlag !== '-' && !('account' in row) && (row.description || row.ledgerCategory)\n if (!inputs.some(wants)) return inputs\n\n const accounts = await tx.getRepository(Account).find({ where: { domain: { id: domain.id }, active: true } as any })\n const match = accountMatcher(accounts)\n const edited = inputs.filter(row => wants(row) && row.id && row.cuFlag !== '+').map(row => row.id)\n const stored = new Map(\n (edited.length ? await tx.getRepository(Transaction).find({ where: { domain: { id: domain.id }, id: In(edited) } as any, select: ['id', 'type', 'category'] as any }) : []).map(row => [row.id, row])\n )\n return inputs.map(row => {\n if (!wants(row)) return row\n const before = stored.get(row.id)\n const account = match(textsOf(row, row.category ?? before?.category), row.type ?? before?.type)\n return account ? { ...row, account: { id: account.id } } : row\n })\n}\n\nexport interface AccountLinking {\n /** Rows given an account, per kind: confirmed transactions, forecasts, recurring items, expenditure request lines. */\n linked: { transactions: number; forecasts: number; recurring: number; expenditureLines: number }\n /** The words of confirmed transactions no account matched. */\n unmatched: string[]\n}\n\n/**\n * Give every row without an account the one its words mean: a confirmed transaction by its description then its\n * ledger category, a forecast by its description, a recurring item by its name, an expenditure request line by its\n * category then its description, among the expense accounts. `apply` false writes nothing. Rows a person already filed\n * are left alone.\n */\nexport async function linkAccounts(\n tx: EntityManager,\n domain: { id: string },\n match: ReturnType<typeof accountMatcher>,\n apply: boolean\n): Promise<AccountLinking> {\n const report: AccountLinking = { linked: { transactions: 0, forecasts: 0, recurring: 0, expenditureLines: 0 }, unmatched: [] }\n\n const run = async <T extends { id: string }>(\n label: keyof AccountLinking['linked'],\n entity: new () => T,\n texts: (row: T) => (string | null | undefined)[],\n typeOf: (row: T) => string | null,\n scope: Record<string, any> = {}\n ) => {\n const rows = (await tx.getRepository(entity as any).find({ where: { domain: { id: domain.id }, ...scope, account: IsNull() } as any })) as T[]\n const groups = new Map<string, string[]>()\n for (const row of rows) {\n const account = match(texts(row), typeOf(row))\n if (!account) {\n if (label === 'transactions') report.unmatched.push(String(texts(row)[0] ?? ''))\n continue\n }\n groups.set(account.id, [...(groups.get(account.id) ?? []), row.id])\n }\n report.linked[label] = [...groups.values()].reduce((sum, ids) => sum + ids.length, 0)\n if (!apply) return\n for (const [id, ids] of groups)\n for (let i = 0; i < ids.length; i += 500) await tx.getRepository(entity as any).update(ids.slice(i, i + 500), { account: { id } } as any)\n }\n\n await run('transactions', Transaction, row => [row.description, row.ledgerCategory], row => row.type, { category: TransactionCategory.CONFIRMED })\n await run('forecasts', Transaction, row => [row.description], row => row.type, { category: TransactionCategory.FORECAST })\n await run('recurring', RecurringCashItem, row => [row.name], row => row.type)\n await run('expenditureLines', ExpenditureRequestItem, row => [row.category, row.description], () => CashFlowType.EXPENSE)\n return report\n}\n"]}
|
|
1
|
+
{"version":3,"file":"account-match.js","sourceRoot":"","sources":["../../../server/service/account/account-match.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In, IsNull } from 'typeorm'\n\nimport { Account } from './account'\n\nimport { CashFlowType } from '../cash-flow-type'\nimport { ExpenditureRequestItem } from '../expenditure-request/expenditure-request-item'\nimport { RecurringCashItem } from '../recurring-cash-item/recurring-cash-item'\nimport { Transaction, TransactionCategory } from '../transaction/transaction'\n\n/*\n * Which account some words mean. An account is known by its name and its aliases; the longest one contained in the\n * text wins, among accounts of the row's direction. Two different accounts matching equally long is no answer, and\n * nothing matching is no answer - a row nobody can place stays unplaced, to be listed, not filed under a catch-all.\n */\n\nexport interface AccountForMatch {\n id: string\n name: string\n type: string\n aliases?: string | null\n}\n\nconst key = (text: string | null | undefined) =>\n String(text ?? '')\n .normalize('NFC')\n .replace(/[\\s\\-_.·,()]+/g, '')\n .toLowerCase()\n\n/** An account's name and its aliases (one per line, or comma separated). */\nexport function aliasesOf(account: Pick<AccountForMatch, 'name' | 'aliases'>): string[] {\n return [account.name, ...String(account.aliases ?? '').split(/[\\n,]/)].map(one => one.trim()).filter(Boolean)\n}\n\n/**\n * A function telling which account some words mean. `type` limits it to that direction - an expense is never filed\n * under an income account because a word happens to match.\n */\nexport function accountMatcher<T extends AccountForMatch>(accounts: readonly T[]) {\n const words: { word: string; account: T }[] = []\n for (const account of accounts) for (const alias of aliasesOf(account)) if (key(alias).length >= 2) words.push({ word: key(alias), account })\n words.sort((a, b) => b.word.length - a.word.length)\n\n return (texts: readonly (string | null | undefined)[], type?: string | null): T | undefined => {\n for (const text of texts) {\n const hay = key(text)\n if (!hay) continue\n const hits = words.filter(one => (!type || one.account.type === type) && hay.includes(one.word))\n if (!hits.length) continue\n const longest = hits[0].word.length\n const top = new Set(hits.filter(one => one.word.length === longest).map(one => one.account.id))\n return top.size === 1 ? hits[0].account : undefined\n }\n return undefined\n }\n}\n\n/**\n * Give each row a person writes that names no account the one its words mean (§`accountMatcher`): a bank line by\n * its description then its ledger category, among the active accounts of its direction\n * - the row's own, or the stored one for a change that does not say. A row that says `account` (an account or none)\n * keeps what it says; a row whose words mean nothing is saved without one.\n */\nexport async function assignAccounts<T extends Record<string, any>>(tx: EntityManager, domain: { id: string }, inputs: T[]): Promise<T[]> {\n const textsOf = (row: Record<string, any>) => [row.description, row.ledgerCategory]\n const wants = (row: Record<string, any>) => row.cuFlag !== '-' && !('account' in row) && (row.description || row.ledgerCategory)\n if (!inputs.some(wants)) return inputs\n\n const accounts = await tx.getRepository(Account).find({ where: { domain: { id: domain.id }, active: true } as any })\n const match = accountMatcher(accounts)\n const edited = inputs.filter(row => wants(row) && row.id && row.cuFlag !== '+').map(row => row.id)\n const stored = new Map(\n (edited.length ? await tx.getRepository(Transaction).find({ where: { domain: { id: domain.id }, id: In(edited) } as any, select: ['id', 'type'] as any }) : []).map(row => [row.id, row])\n )\n return inputs.map(row => {\n if (!wants(row)) return row\n const before = stored.get(row.id)\n const account = match(textsOf(row), row.type ?? before?.type)\n return account ? { ...row, account: { id: account.id } } : row\n })\n}\n\nexport interface AccountLinking {\n /** Rows given an account, per kind: bank lines, recurring items, expenditure request lines. */\n linked: { transactions: number; recurring: number; expenditureLines: number }\n /** The words of confirmed transactions no account matched. */\n unmatched: string[]\n}\n\n/**\n * Give every row without an account the one its words mean: a confirmed transaction by its description then its\n * ledger category, a recurring item by its name, an expenditure request line by its\n * category then its description, among the expense accounts. `apply` false writes nothing. Rows a person already filed\n * are left alone.\n */\nexport async function linkAccounts(\n tx: EntityManager,\n domain: { id: string },\n match: ReturnType<typeof accountMatcher>,\n apply: boolean\n): Promise<AccountLinking> {\n const report: AccountLinking = { linked: { transactions: 0, recurring: 0, expenditureLines: 0 }, unmatched: [] }\n\n const run = async <T extends { id: string }>(\n label: keyof AccountLinking['linked'],\n entity: new () => T,\n texts: (row: T) => (string | null | undefined)[],\n typeOf: (row: T) => string | null,\n scope: Record<string, any> = {}\n ) => {\n const rows = (await tx.getRepository(entity as any).find({ where: { domain: { id: domain.id }, ...scope, account: IsNull() } as any })) as T[]\n const groups = new Map<string, string[]>()\n for (const row of rows) {\n const account = match(texts(row), typeOf(row))\n if (!account) {\n if (label === 'transactions') report.unmatched.push(String(texts(row)[0] ?? ''))\n continue\n }\n groups.set(account.id, [...(groups.get(account.id) ?? []), row.id])\n }\n report.linked[label] = [...groups.values()].reduce((sum, ids) => sum + ids.length, 0)\n if (!apply) return\n for (const [id, ids] of groups)\n for (let i = 0; i < ids.length; i += 500) await tx.getRepository(entity as any).update(ids.slice(i, i + 500), { account: { id } } as any)\n }\n\n await run('transactions', Transaction, row => [row.description, row.ledgerCategory], row => row.type, { category: TransactionCategory.CONFIRMED })\n await run('recurring', RecurringCashItem, row => [row.name], row => row.type)\n await run('expenditureLines', ExpenditureRequestItem, row => [row.category, row.description], () => CashFlowType.EXPENSE)\n return report\n}\n"]}
|
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AccountMutation = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
|
-
const typeorm_1 = require("typeorm");
|
|
7
6
|
const shell_1 = require("@things-factory/shell");
|
|
8
7
|
const account_1 = require("./account");
|
|
9
8
|
const account_type_1 = require("./account-type");
|
|
9
|
+
const account_service_1 = require("./account-service");
|
|
10
10
|
let AccountMutation = class AccountMutation {
|
|
11
11
|
async createAccount(account, context) {
|
|
12
12
|
const { domain, user, tx } = context.state;
|
|
@@ -70,15 +70,12 @@ let AccountMutation = class AccountMutation {
|
|
|
70
70
|
}
|
|
71
71
|
async deleteAccount(id, context) {
|
|
72
72
|
const { domain, tx } = context.state;
|
|
73
|
-
await (0,
|
|
73
|
+
await (0, account_service_1.deleteAccounts)(tx, domain, [id]);
|
|
74
74
|
return true;
|
|
75
75
|
}
|
|
76
76
|
async deleteAccounts(ids, context) {
|
|
77
77
|
const { domain, tx } = context.state;
|
|
78
|
-
await (0,
|
|
79
|
-
domain: { id: domain.id },
|
|
80
|
-
id: (0, typeorm_1.In)(ids)
|
|
81
|
-
});
|
|
78
|
+
await (0, account_service_1.deleteAccounts)(tx, domain, ids);
|
|
82
79
|
return true;
|
|
83
80
|
}
|
|
84
81
|
async importAccounts(accounts, context) {
|
|
@@ -152,6 +149,7 @@ tslib_1.__decorate([
|
|
|
152
149
|
tslib_1.__metadata("design:returntype", Promise)
|
|
153
150
|
], AccountMutation.prototype, "importAccounts", null);
|
|
154
151
|
exports.AccountMutation = AccountMutation = tslib_1.__decorate([
|
|
155
|
-
(0, type_graphql_1.Resolver)(account_1.Account)
|
|
152
|
+
(0, type_graphql_1.Resolver)(account_1.Account),
|
|
153
|
+
(0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
|
|
156
154
|
], AccountMutation);
|
|
157
155
|
//# sourceMappingURL=account-mutation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-mutation.js","sourceRoot":"","sources":["../../../server/service/account/account-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Account } from './account'\nimport { NewAccount, AccountPatch } from './account-type'\n\n@Resolver(Account)\nexport class AccountMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Account, { description: 'To create new Account' })\n async createAccount(@Arg('account') account: NewAccount, @Ctx() context: ResolverContext): Promise<Account> {\n const { domain, user, tx } = context.state\n\n const result = await getRepository(Account, tx).save({\n ...account,\n domain,\n creator: user,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Account, { description: 'To modify Account information' })\n async updateAccount(\n @Arg('id') id: string,\n @Arg('patch') patch: AccountPatch,\n @Ctx() context: ResolverContext\n ): Promise<Account> {\n const { domain, user, tx } = context.state\n\n const repository = getRepository(Account, tx)\n const account = await repository.findOne({\n where: { domain: { id: domain.id }, id }\n })\n\n const result = await repository.save({\n ...account,\n ...patch,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => [Account], { description: \"To modify multiple Accounts' information\" })\n async updateMultipleAccount(\n @Arg('patches', type => [AccountPatch]) patches: AccountPatch[],\n @Ctx() context: ResolverContext\n ): Promise<Account[]> {\n const { domain, user, tx } = context.state\n\n let results = []\n const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')\n const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')\n const accountRepo = getRepository(Account, tx)\n\n if (_createRecords.length > 0) {\n for (let i = 0; i < _createRecords.length; i++) {\n const newRecord = _createRecords[i]\n\n const result = await accountRepo.save({\n ...newRecord,\n domain,\n creator: user,\n updater: user\n })\n\n results.push({ ...result, cuFlag: '+' })\n }\n }\n\n if (_updateRecords.length > 0) {\n for (let i = 0; i < _updateRecords.length; i++) {\n const updateRecord = _updateRecords[i]\n const account = await accountRepo.findOne({\n where: { domain: { id: domain.id }, id: updateRecord.id }\n })\n\n if (!account) {\n throw new Error(`failed to find account to update: ${updateRecord.id}`)\n }\n\n const result = await accountRepo.save({\n ...account,\n ...updateRecord,\n updater: user\n })\n\n results.push({ ...result, cuFlag: 'M' })\n }\n }\n\n return results\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete Account' })\n async deleteAccount(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await getRepository(Account, tx).softDelete({ domain: { id: domain.id }, id })\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple Accounts' })\n async deleteAccounts(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await getRepository(Account, tx).softDelete({\n domain: { id: domain.id },\n id: In(ids)\n })\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To import multiple Accounts' })\n async importAccounts(\n @Arg('accounts', type => [AccountPatch]) accounts: AccountPatch[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n\n await Promise.all(\n accounts.map(async (account: AccountPatch) => {\n const createdAccount: Account = await getRepository(Account, tx).save({ domain, ...account })\n })\n )\n\n return true\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"account-mutation.js","sourceRoot":"","sources":["../../../server/service/account/account-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive, UseMiddleware } from 'type-graphql'\n\nimport { getRepository, SpeakRefusals } from '@things-factory/shell'\n\nimport { Account } from './account'\nimport { NewAccount, AccountPatch } from './account-type'\nimport { deleteAccounts } from './account-service'\n\n@Resolver(Account)\n@UseMiddleware(SpeakRefusals)\nexport class AccountMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Account, { description: 'To create new Account' })\n async createAccount(@Arg('account') account: NewAccount, @Ctx() context: ResolverContext): Promise<Account> {\n const { domain, user, tx } = context.state\n\n const result = await getRepository(Account, tx).save({\n ...account,\n domain,\n creator: user,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Account, { description: 'To modify Account information' })\n async updateAccount(\n @Arg('id') id: string,\n @Arg('patch') patch: AccountPatch,\n @Ctx() context: ResolverContext\n ): Promise<Account> {\n const { domain, user, tx } = context.state\n\n const repository = getRepository(Account, tx)\n const account = await repository.findOne({\n where: { domain: { id: domain.id }, id }\n })\n\n const result = await repository.save({\n ...account,\n ...patch,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => [Account], { description: \"To modify multiple Accounts' information\" })\n async updateMultipleAccount(\n @Arg('patches', type => [AccountPatch]) patches: AccountPatch[],\n @Ctx() context: ResolverContext\n ): Promise<Account[]> {\n const { domain, user, tx } = context.state\n\n let results = []\n const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')\n const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')\n const accountRepo = getRepository(Account, tx)\n\n if (_createRecords.length > 0) {\n for (let i = 0; i < _createRecords.length; i++) {\n const newRecord = _createRecords[i]\n\n const result = await accountRepo.save({\n ...newRecord,\n domain,\n creator: user,\n updater: user\n })\n\n results.push({ ...result, cuFlag: '+' })\n }\n }\n\n if (_updateRecords.length > 0) {\n for (let i = 0; i < _updateRecords.length; i++) {\n const updateRecord = _updateRecords[i]\n const account = await accountRepo.findOne({\n where: { domain: { id: domain.id }, id: updateRecord.id }\n })\n\n if (!account) {\n throw new Error(`failed to find account to update: ${updateRecord.id}`)\n }\n\n const result = await accountRepo.save({\n ...account,\n ...updateRecord,\n updater: user\n })\n\n results.push({ ...result, cuFlag: 'M' })\n }\n }\n\n return results\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete Account' })\n async deleteAccount(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await deleteAccounts(tx, domain, [id])\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple Accounts' })\n async deleteAccounts(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await deleteAccounts(tx, domain, ids)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"accounting-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To import multiple Accounts' })\n async importAccounts(\n @Arg('accounts', type => [AccountPatch]) accounts: AccountPatch[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n\n await Promise.all(\n accounts.map(async (account: AccountPatch) => {\n const createdAccount: Account = await getRepository(Account, tx).save({ domain, ...account })\n })\n )\n\n return true\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteAccounts = deleteAccounts;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
const shell_1 = require("@things-factory/shell");
|
|
6
|
+
const account_1 = require("./account");
|
|
7
|
+
/** An account a line, a recurring item or another row still names is not deleted (§`refuseTiedRows`). */
|
|
8
|
+
async function deleteAccounts(tx, domain, ids) {
|
|
9
|
+
const unique = [...new Set(ids)];
|
|
10
|
+
const repo = tx.getRepository(account_1.Account);
|
|
11
|
+
const found = await repo.find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(unique) } });
|
|
12
|
+
if (found.length !== unique.length)
|
|
13
|
+
throw new shell_1.Refusal('account-not-found', {}, 'an account was not found - reload and try again');
|
|
14
|
+
await (0, shell_1.refuseTiedRows)(tx, account_1.Account, unique);
|
|
15
|
+
await repo.softRemove(found);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=account-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-service.js","sourceRoot":"","sources":["../../../server/service/account/account-service.ts"],"names":[],"mappings":";;AAOA,wCAOC;AAdD,qCAA2C;AAE3C,iDAA+D;AAE/D,uCAAmC;AAEnC,yGAAyG;AAClG,KAAK,UAAU,cAAc,CAAC,EAAiB,EAAE,MAAsB,EAAE,GAAa;IAC3F,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,EAAE,CAAC,aAAa,CAAC,iBAAO,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;IACvF,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QAAE,MAAM,IAAI,eAAO,CAAC,mBAAmB,EAAE,EAAE,EAAE,iDAAiD,CAAC,CAAA;IACjI,MAAM,IAAA,sBAAc,EAAC,EAAE,EAAE,iBAAO,EAAE,MAAM,CAAC,CAAA;IACzC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { Account } from './account'\n\n/** An account a line, a recurring item or another row still names is not deleted (§`refuseTiedRows`). */\nexport async function deleteAccounts(tx: EntityManager, domain: { id: string }, ids: string[]): Promise<void> {\n const unique = [...new Set(ids)]\n const repo = tx.getRepository(Account)\n const found = await repo.find({ where: { domain: { id: domain.id }, id: In(unique) } })\n if (found.length !== unique.length) throw new Refusal('account-not-found', {}, 'an account was not found - reload and try again')\n await refuseTiedRows(tx, Account, unique)\n await repo.softRemove(found)\n}\n"]}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { EntityManager } from 'typeorm';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* An account that lines were written on is made inactive, not deleted - the lines keep pointing at it, deleted ones
|
|
4
|
+
* too, since a deleted financial row stays readable. Whatever else names it stops the delete (§`refuseTiedRows`).
|
|
5
|
+
*/
|
|
3
6
|
export declare function deleteBankAccounts(tx: EntityManager, domain: {
|
|
4
7
|
id: string;
|
|
5
8
|
}, ids: string[]): Promise<void>;
|
|
@@ -5,7 +5,10 @@ const typeorm_1 = require("typeorm");
|
|
|
5
5
|
const shell_1 = require("@things-factory/shell");
|
|
6
6
|
const transaction_1 = require("../transaction/transaction");
|
|
7
7
|
const bank_account_1 = require("./bank-account");
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* An account that lines were written on is made inactive, not deleted - the lines keep pointing at it, deleted ones
|
|
10
|
+
* too, since a deleted financial row stays readable. Whatever else names it stops the delete (§`refuseTiedRows`).
|
|
11
|
+
*/
|
|
9
12
|
async function deleteBankAccounts(tx, domain, ids) {
|
|
10
13
|
const repo = tx.getRepository(bank_account_1.BankAccount);
|
|
11
14
|
const found = await repo.find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(ids) } });
|
|
@@ -16,6 +19,7 @@ async function deleteBankAccounts(tx, domain, ids) {
|
|
|
16
19
|
.count({ where: { domain: { id: domain.id }, bankAccount: { id: (0, typeorm_1.In)(ids) } }, withDeleted: true });
|
|
17
20
|
if (used)
|
|
18
21
|
throw new shell_1.Refusal('bank-account-has-transactions', {}, 'transactions were written on the account - make it inactive instead');
|
|
22
|
+
await (0, shell_1.refuseTiedRows)(tx, bank_account_1.BankAccount, ids);
|
|
19
23
|
await repo.softRemove(found);
|
|
20
24
|
}
|
|
21
25
|
//# sourceMappingURL=bank-account-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bank-account-service.js","sourceRoot":"","sources":["../../../server/service/bank-account/bank-account-service.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"bank-account-service.js","sourceRoot":"","sources":["../../../server/service/bank-account/bank-account-service.ts"],"names":[],"mappings":";;AAWA,gDAgBC;AA3BD,qCAA2C;AAE3C,iDAA+D;AAE/D,4DAAwD;AACxD,iDAA4C;AAE5C;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CAAC,EAAiB,EAAE,MAAsB,EAAE,GAAa;IAC/F,MAAM,IAAI,GAAG,EAAE,CAAC,aAAa,CAAC,0BAAW,CAAC,CAAA;IAC1C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;IACpF,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI;QACpC,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,qDAAqD,CAAC,CAAA;IACxG,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,aAAa,CAAC,yBAAW,CAAC;SAC1B,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IACnG,IAAI,IAAI;QACN,MAAM,IAAI,eAAO,CACf,+BAA+B,EAC/B,EAAE,EACF,qEAAqE,CACtE,CAAA;IACH,MAAM,IAAA,sBAAc,EAAC,EAAE,EAAE,0BAAW,EAAE,GAAG,CAAC,CAAA;IAC1C,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { Transaction } from '../transaction/transaction'\nimport { BankAccount } from './bank-account'\n\n/**\n * An account that lines were written on is made inactive, not deleted - the lines keep pointing at it, deleted ones\n * too, since a deleted financial row stays readable. Whatever else names it stops the delete (§`refuseTiedRows`).\n */\nexport async function deleteBankAccounts(tx: EntityManager, domain: { id: string }, ids: string[]): Promise<void> {\n const repo = tx.getRepository(BankAccount)\n const found = await repo.find({ where: { domain: { id: domain.id }, id: In(ids) } })\n if (found.length !== new Set(ids).size)\n throw new Refusal('bank-account-not-found', {}, 'a bank account was not found - reload and try again')\n const used = await tx\n .getRepository(Transaction)\n .count({ where: { domain: { id: domain.id }, bankAccount: { id: In(ids) } }, withDeleted: true })\n if (used)\n throw new Refusal(\n 'bank-account-has-transactions',\n {},\n 'transactions were written on the account - make it inactive instead'\n )\n await refuseTiedRows(tx, BankAccount, ids)\n await repo.softRemove(found)\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Domain } from '@things-factory/shell';
|
|
2
2
|
import { User } from '@things-factory/auth-base';
|
|
3
3
|
import { TaxInvoice } from '@things-factory/tax-invoice';
|
|
4
|
+
import { Attachment } from '@things-factory/attachment-base';
|
|
4
5
|
import { Account } from '../account/account';
|
|
5
6
|
import { ExpenditureRequest } from './expenditure-request';
|
|
6
7
|
export declare enum ExpenditurePaymentMethod {
|
|
@@ -10,8 +11,8 @@ export declare enum ExpenditurePaymentMethod {
|
|
|
10
11
|
TRANSFER = "TRANSFER"
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
|
-
* One thing a request pays for - a meal, a taxi, a supplier's invoice.
|
|
14
|
-
* tax invoice it pays is cited, and then the bank line pays that invoice (ADR-0135 §2-5).
|
|
14
|
+
* One thing a request pays for - a meal, a taxi, a supplier's invoice. Its receipts and papers are attached to it by
|
|
15
|
+
* id; a purchase tax invoice it pays is cited, and then the bank line pays that invoice (ADR-0135 §2-5).
|
|
15
16
|
*/
|
|
16
17
|
export declare class ExpenditureRequestItem {
|
|
17
18
|
readonly id: string;
|
|
@@ -30,7 +31,7 @@ export declare class ExpenditureRequestItem {
|
|
|
30
31
|
paymentMethod: ExpenditurePaymentMethod;
|
|
31
32
|
taxInvoice?: TaxInvoice;
|
|
32
33
|
taxInvoiceId?: string;
|
|
33
|
-
|
|
34
|
+
files?: Attachment[];
|
|
34
35
|
createdAt?: Date;
|
|
35
36
|
updatedAt?: Date;
|
|
36
37
|
deletedAt?: Date;
|
|
@@ -7,6 +7,7 @@ const type_graphql_1 = require("type-graphql");
|
|
|
7
7
|
const shell_1 = require("@things-factory/shell");
|
|
8
8
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
9
9
|
const tax_invoice_1 = require("@things-factory/tax-invoice");
|
|
10
|
+
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
10
11
|
const account_1 = require("../account/account");
|
|
11
12
|
const expenditure_request_1 = require("./expenditure-request");
|
|
12
13
|
var ExpenditurePaymentMethod;
|
|
@@ -21,8 +22,8 @@ var ExpenditurePaymentMethod;
|
|
|
21
22
|
description: 'How a line was paid - the corporate card is reported and approved, but the company already paid it'
|
|
22
23
|
});
|
|
23
24
|
/**
|
|
24
|
-
* One thing a request pays for - a meal, a taxi, a supplier's invoice.
|
|
25
|
-
* tax invoice it pays is cited, and then the bank line pays that invoice (ADR-0135 §2-5).
|
|
25
|
+
* One thing a request pays for - a meal, a taxi, a supplier's invoice. Its receipts and papers are attached to it by
|
|
26
|
+
* id; a purchase tax invoice it pays is cited, and then the bank line pays that invoice (ADR-0135 §2-5).
|
|
26
27
|
*/
|
|
27
28
|
let ExpenditureRequestItem = class ExpenditureRequestItem {
|
|
28
29
|
};
|
|
@@ -105,9 +106,9 @@ tslib_1.__decorate([
|
|
|
105
106
|
tslib_1.__metadata("design:type", String)
|
|
106
107
|
], ExpenditureRequestItem.prototype, "taxInvoiceId", void 0);
|
|
107
108
|
tslib_1.__decorate([
|
|
108
|
-
(0, type_graphql_1.Field)({ nullable: true, description: '
|
|
109
|
-
tslib_1.__metadata("design:type",
|
|
110
|
-
], ExpenditureRequestItem.prototype, "
|
|
109
|
+
(0, type_graphql_1.Field)(type => [attachment_base_1.Attachment], { nullable: true, description: 'The receipts and papers the line is proven by, the first attached first' }),
|
|
110
|
+
tslib_1.__metadata("design:type", Array)
|
|
111
|
+
], ExpenditureRequestItem.prototype, "files", void 0);
|
|
111
112
|
tslib_1.__decorate([
|
|
112
113
|
(0, typeorm_1.CreateDateColumn)(),
|
|
113
114
|
(0, type_graphql_1.Field)({ nullable: true, description: 'When it was written' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expenditure-request-item.js","sourceRoot":"","sources":["../../../server/service/expenditure-request/expenditure-request-item.ts"],"names":[],"mappings":";;;;AAAA,qCAUgB;AAChB,+CAAwF;AAExF,iDAA2D;AAC3D,yDAAgD;AAChD,6DAAwD;
|
|
1
|
+
{"version":3,"file":"expenditure-request-item.js","sourceRoot":"","sources":["../../../server/service/expenditure-request/expenditure-request-item.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Directive, Field, Float, ID, ObjectType, registerEnumType } from 'type-graphql'\n\nimport { Domain, moneyColumn } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { TaxInvoice } from '@things-factory/tax-invoice'\nimport { Attachment } from '@things-factory/attachment-base'\n\nimport { Account } from '../account/account'\nimport { ExpenditureRequest } from './expenditure-request'\n\nexport enum ExpenditurePaymentMethod {\n PERSONAL_CARD = 'PERSONAL_CARD',\n CORPORATE_CARD = 'CORPORATE_CARD',\n CASH = 'CASH',\n TRANSFER = 'TRANSFER'\n}\n\nregisterEnumType(ExpenditurePaymentMethod, {\n name: 'ExpenditurePaymentMethod',\n description: 'How a line was paid - the corporate card is reported and approved, but the company already paid it'\n})\n\n/**\n * One thing a request pays for - a meal, a taxi, a supplier's invoice. Its receipts and papers are attached to it by\n * id; a purchase tax invoice it pays is cited, and then the bank line pays that invoice (ADR-0135 §2-5).\n */\n@Entity()\n@Index('ix_expenditure_request_item_0', (item: ExpenditureRequestItem) => [item.domain, item.request])\n@Index('ix_expenditure_request_item_1', (item: ExpenditureRequestItem) => [item.domain, item.taxInvoice])\n@ObjectType({ description: 'One thing an expenditure request pays for' })\nexport class ExpenditureRequestItem {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain it belongs to' })\n domain?: Domain\n\n @RelationId((item: ExpenditureRequestItem) => item.domain)\n domainId?: string\n\n @ManyToOne(type => ExpenditureRequest, request => request.items, { nullable: false, onDelete: 'CASCADE' })\n @Field(type => ExpenditureRequest, { nullable: true, description: 'The request the line is on' })\n request?: ExpenditureRequest\n\n @RelationId((item: ExpenditureRequestItem) => item.request)\n requestId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'YYYY-MM-DD it was spent, or the day of the invoice' })\n spentDate?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"What the requester called it - '식대', '교통비' - until an account is chosen\" })\n category?: string\n\n @ManyToOne(type => Account, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Account, { nullable: true, description: '계정과목' })\n account?: Account\n\n @RelationId((item: ExpenditureRequestItem) => item.account)\n accountId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Where - the shop or company on the receipt' })\n merchant?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'What for - the purpose an approver judges' })\n description?: string\n\n @Column(moneyColumn())\n @Field(type => Float, { description: 'Amount paid, VAT included' })\n amount: number\n\n @Column(moneyColumn({ nullable: true, default: null }))\n @Field(type => Float, { nullable: true, description: 'VAT in the amount, when the receipt shows it' })\n vatAmount?: number\n\n @Column({ default: ExpenditurePaymentMethod.PERSONAL_CARD })\n @Field(type => ExpenditurePaymentMethod, { description: 'How it was paid' })\n paymentMethod: ExpenditurePaymentMethod\n\n /* The tax-invoice ledger's row (ADR-0139): a requester without it sees the line, and FORBIDDEN on this path. */\n @ManyToOne(type => TaxInvoice, { nullable: true })\n @Directive('@privilege(category: \"tax-invoice\", privilege: \"query\", domainOwnerGranted: false)')\n @Field(type => TaxInvoice, { nullable: true, description: 'The purchase tax invoice this line pays' })\n taxInvoice?: TaxInvoice\n\n @RelationId((item: ExpenditureRequestItem) => item.taxInvoice)\n taxInvoiceId?: string\n\n @Field(type => [Attachment], { nullable: true, description: 'The receipts and papers the line is proven by, the first attached first' })\n files?: Attachment[]\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When it was written' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When it was last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When it was deleted - financial rows are kept, not erased' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who wrote it' })\n creator?: User\n\n @RelationId((item: ExpenditureRequestItem) => item.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who changed it last' })\n updater?: User\n\n @RelationId((item: ExpenditureRequestItem) => item.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -4,10 +4,10 @@ import { ExpenditureRequestItem } from './expenditure-request-item';
|
|
|
4
4
|
import { ExpenditurePaymentInput, ExpenditureRequestInput, ExpenditureRequestItemPatch, NewExpenditureLine } from './expenditure-request-type';
|
|
5
5
|
export declare class ExpenditureRequestMutation {
|
|
6
6
|
/** Write a request and its lines. A new request is the caller's; an existing one only its requester's, while editable. */
|
|
7
|
-
saveExpenditureRequest(request: ExpenditureRequestInput, items: ExpenditureRequestItemPatch[], context: ResolverContext): Promise<ExpenditureRequest>;
|
|
7
|
+
saveExpenditureRequest(request: ExpenditureRequestInput, items: ExpenditureRequestItemPatch[], unpaidInvoicesOnly: boolean, context: ResolverContext): Promise<ExpenditureRequest>;
|
|
8
8
|
/** One line from the phone - it joins this month's editable reimbursement, or starts one. */
|
|
9
9
|
addExpenditureLine(line: NewExpenditureLine, context: ResolverContext): Promise<ExpenditureRequest>;
|
|
10
|
-
|
|
10
|
+
saveExpenditureLineFiles(itemId: string, attach: FileUpload[], remove: string[], context: ResolverContext): Promise<ExpenditureRequestItem>;
|
|
11
11
|
submitExpenditureRequest(id: string, context: ResolverContext): Promise<ExpenditureRequest>;
|
|
12
12
|
withdrawExpenditureRequest(id: string, context: ResolverContext): Promise<ExpenditureRequest>;
|
|
13
13
|
deleteExpenditureRequest(id: string, context: ResolverContext): Promise<boolean>;
|