@things-factory/sales 10.1.244 → 10.1.246
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-client/close-months.d.ts +5 -0
- package/dist-client/close-months.js +20 -0
- package/dist-client/close-months.js.map +1 -0
- package/dist-client/headroom-choice.d.ts +10 -0
- package/dist-client/headroom-choice.js +22 -0
- package/dist-client/headroom-choice.js.map +1 -0
- package/dist-client/pages/opportunity-list.d.ts +18 -3
- package/dist-client/pages/opportunity-list.js +93 -9
- package/dist-client/pages/opportunity-list.js.map +1 -1
- package/dist-client/pages/quotation-list.d.ts +11 -0
- package/dist-client/pages/quotation-list.js +71 -5
- package/dist-client/pages/quotation-list.js.map +1 -1
- package/dist-client/pages/sales-product-list.d.ts +6 -1
- package/dist-client/pages/sales-product-list.js +42 -2
- package/dist-client/pages/sales-product-list.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/opportunity/opportunity-query.d.ts +6 -0
- package/dist-server/service/opportunity/opportunity-query.js +14 -1
- package/dist-server/service/opportunity/opportunity-query.js.map +1 -1
- package/dist-server/service/opportunity/opportunity-service.d.ts +13 -1
- package/dist-server/service/opportunity/opportunity-service.js +13 -0
- package/dist-server/service/opportunity/opportunity-service.js.map +1 -1
- package/dist-server/service/opportunity/opportunity-type.d.ts +5 -0
- package/dist-server/service/opportunity/opportunity-type.js +19 -1
- package/dist-server/service/opportunity/opportunity-type.js.map +1 -1
- package/dist-server/service/quotation/quotation-query.d.ts +4 -0
- package/dist-server/service/quotation/quotation-query.js +13 -1
- package/dist-server/service/quotation/quotation-query.js.map +1 -1
- package/dist-server/service/quotation/quotation-service.d.ts +9 -2
- package/dist-server/service/quotation/quotation-service.js +9 -0
- package/dist-server/service/quotation/quotation-service.js.map +1 -1
- package/dist-server/service/sales-product/sales-product-query.d.ts +4 -0
- package/dist-server/service/sales-product/sales-product-query.js +14 -1
- package/dist-server/service/sales-product/sales-product-query.js.map +1 -1
- package/dist-server/service/sales-product/sales-product-service.d.ts +11 -1
- package/dist-server/service/sales-product/sales-product-service.js +16 -0
- package/dist-server/service/sales-product/sales-product-service.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/translations/en.json +8 -0
- package/translations/ko.json +8 -0
|
@@ -4,10 +4,16 @@ import { Domain, ListParam } from '@things-factory/shell';
|
|
|
4
4
|
import { Quotation } from '../quotation/quotation';
|
|
5
5
|
import { Opportunity } from './opportunity';
|
|
6
6
|
import { OpportunityEvent } from './opportunity-event';
|
|
7
|
+
import { OpportunityStage } from './opportunity-rules';
|
|
7
8
|
import { OpportunityList, OpportunityStageDefault } from './opportunity-type';
|
|
8
9
|
export declare class OpportunityQuery {
|
|
9
10
|
opportunity(id: string, context: ResolverContext): Promise<Opportunity>;
|
|
10
11
|
opportunities(params: ListParam, context: ResolverContext): Promise<OpportunityList>;
|
|
12
|
+
opportunityStageCounts(params: ListParam, context: ResolverContext): Promise<{
|
|
13
|
+
stage: OpportunityStage;
|
|
14
|
+
count: number;
|
|
15
|
+
open: boolean;
|
|
16
|
+
}[]>;
|
|
11
17
|
opportunityEvents(opportunityId: string, context: ResolverContext): Promise<OpportunityEvent[]>;
|
|
12
18
|
opportunityStages(): OpportunityStageDefault[];
|
|
13
19
|
opportunityOwners(context: ResolverContext): Promise<User[]>;
|
|
@@ -11,6 +11,7 @@ const quotation_service_1 = require("../quotation/quotation-service");
|
|
|
11
11
|
const opportunity_1 = require("./opportunity");
|
|
12
12
|
const opportunity_event_1 = require("./opportunity-event");
|
|
13
13
|
const opportunity_rules_1 = require("./opportunity-rules");
|
|
14
|
+
const opportunity_service_1 = require("./opportunity-service");
|
|
14
15
|
const opportunity_type_1 = require("./opportunity-type");
|
|
15
16
|
let OpportunityQuery = class OpportunityQuery {
|
|
16
17
|
opportunity(id, context) {
|
|
@@ -21,11 +22,14 @@ let OpportunityQuery = class OpportunityQuery {
|
|
|
21
22
|
domain: context.state.domain,
|
|
22
23
|
params,
|
|
23
24
|
repository: (0, shell_1.getRepository)(opportunity_1.Opportunity),
|
|
24
|
-
searchables:
|
|
25
|
+
searchables: opportunity_service_1.OPPORTUNITY_SEARCHABLES
|
|
25
26
|
});
|
|
26
27
|
const [items, total] = await query.getManyAndCount();
|
|
27
28
|
return { items, total };
|
|
28
29
|
}
|
|
30
|
+
opportunityStageCounts(params, context) {
|
|
31
|
+
return (0, opportunity_service_1.opportunityStageCounts)((0, shell_1.getRepository)(opportunity_1.Opportunity).manager, context.state.domain, params);
|
|
32
|
+
}
|
|
29
33
|
opportunityEvents(opportunityId, context) {
|
|
30
34
|
return (0, shell_1.getRepository)(opportunity_event_1.OpportunityEvent).find({ where: { domain: { id: context.state.domain.id }, opportunity: { id: opportunityId } }, order: { createdAt: 'ASC' } });
|
|
31
35
|
}
|
|
@@ -76,6 +80,15 @@ tslib_1.__decorate([
|
|
|
76
80
|
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
77
81
|
tslib_1.__metadata("design:returntype", Promise)
|
|
78
82
|
], OpportunityQuery.prototype, "opportunities", null);
|
|
83
|
+
tslib_1.__decorate([
|
|
84
|
+
(0, type_graphql_1.Directive)('@privilege(category: "sales", privilege: "query", domainOwnerGranted: true)'),
|
|
85
|
+
(0, type_graphql_1.Query)(type => [opportunity_type_1.OpportunityStageCount], { description: 'To count the opportunities in each stage, under the list conditions other than the stage, every stage in order' }),
|
|
86
|
+
tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
|
|
87
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
88
|
+
tslib_1.__metadata("design:type", Function),
|
|
89
|
+
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
90
|
+
tslib_1.__metadata("design:returntype", void 0)
|
|
91
|
+
], OpportunityQuery.prototype, "opportunityStageCounts", null);
|
|
79
92
|
tslib_1.__decorate([
|
|
80
93
|
(0, type_graphql_1.Directive)('@privilege(category: "sales", privilege: "query", domainOwnerGranted: true)'),
|
|
81
94
|
(0, type_graphql_1.Query)(type => [opportunity_event_1.OpportunityEvent], { description: 'To fetch what happened to an opportunity, oldest first' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opportunity-query.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDomainAssignees, User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { Quotation } from '../quotation/quotation'\nimport { quotationsOfOpportunity } from '../quotation/quotation-service'\nimport { Opportunity } from './opportunity'\nimport { OpportunityEvent } from './opportunity-event'\nimport { isOpen, OpportunityStage, STAGE_PROBABILITY } from './opportunity-rules'\nimport { OpportunityList, OpportunityStageDefault } from './opportunity-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Opportunity)\nexport class OpportunityQuery {\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Opportunity, { nullable: true, description: 'To fetch an opportunity' })\n opportunity(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Opportunity).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => OpportunityList, { description: 'To fetch a page of opportunities' })\n async opportunities(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OpportunityList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Opportunity),\n searchables: ['name', 'description']\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [OpportunityEvent], { description: 'To fetch what happened to an opportunity, oldest first' })\n opportunityEvents(@Arg('opportunityId') opportunityId: string, @Ctx() context: ResolverContext) {\n return getRepository(OpportunityEvent).find({ where: { domain: { id: context.state.domain.id }, opportunity: { id: opportunityId } }, order: { createdAt: 'ASC' } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [OpportunityStageDefault], { description: 'To fetch the stages in order, with the probability each gives' })\n opportunityStages(): OpportunityStageDefault[] {\n return Object.values(OpportunityStage).map(stage => ({ stage, probability: STAGE_PROBABILITY[stage], open: isOpen(stage) }))\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [User], { description: 'To fetch the people an opportunity may be put in the charge of' })\n opportunityOwners(@Ctx() context: ResolverContext) {\n return getDomainAssignees(context.state.domain)\n }\n\n @FieldResolver(type => [Quotation], { description: 'The quotations that answer the opportunity, every version' })\n quotations(@Root() row: Opportunity) {\n return quotationsOfOpportunity(undefined, { id: row.domainId } as Domain, row.id)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n counterparty(@Root() row: Opportunity) {\n return row.counterparty ?? (row.counterpartyId ? getRepository(Counterparty).findOneBy({ id: row.counterpartyId }) : undefined)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n endCustomer(@Root() row: Opportunity) {\n return row.endCustomer ?? (row.endCustomerId ? getRepository(Counterparty).findOneBy({ id: row.endCustomerId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n owner(@Root() row: Opportunity) {\n return row.owner ?? (row.ownerId ? getRepository(User).findOneBy({ id: row.ownerId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Opportunity) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Opportunity) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Opportunity) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"opportunity-query.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDomainAssignees, User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { Quotation } from '../quotation/quotation'\nimport { quotationsOfOpportunity } from '../quotation/quotation-service'\nimport { Opportunity } from './opportunity'\nimport { OpportunityEvent } from './opportunity-event'\nimport { isOpen, OpportunityStage, STAGE_PROBABILITY } from './opportunity-rules'\nimport { OPPORTUNITY_SEARCHABLES, opportunityStageCounts } from './opportunity-service'\nimport { OpportunityList, OpportunityStageCount, OpportunityStageDefault } from './opportunity-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Opportunity)\nexport class OpportunityQuery {\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Opportunity, { nullable: true, description: 'To fetch an opportunity' })\n opportunity(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Opportunity).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => OpportunityList, { description: 'To fetch a page of opportunities' })\n async opportunities(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OpportunityList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Opportunity),\n searchables: OPPORTUNITY_SEARCHABLES\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [OpportunityStageCount], { description: 'To count the opportunities in each stage, under the list conditions other than the stage, every stage in order' })\n opportunityStageCounts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext) {\n return opportunityStageCounts(getRepository(Opportunity).manager, context.state.domain, params)\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [OpportunityEvent], { description: 'To fetch what happened to an opportunity, oldest first' })\n opportunityEvents(@Arg('opportunityId') opportunityId: string, @Ctx() context: ResolverContext) {\n return getRepository(OpportunityEvent).find({ where: { domain: { id: context.state.domain.id }, opportunity: { id: opportunityId } }, order: { createdAt: 'ASC' } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [OpportunityStageDefault], { description: 'To fetch the stages in order, with the probability each gives' })\n opportunityStages(): OpportunityStageDefault[] {\n return Object.values(OpportunityStage).map(stage => ({ stage, probability: STAGE_PROBABILITY[stage], open: isOpen(stage) }))\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [User], { description: 'To fetch the people an opportunity may be put in the charge of' })\n opportunityOwners(@Ctx() context: ResolverContext) {\n return getDomainAssignees(context.state.domain)\n }\n\n @FieldResolver(type => [Quotation], { description: 'The quotations that answer the opportunity, every version' })\n quotations(@Root() row: Opportunity) {\n return quotationsOfOpportunity(undefined, { id: row.domainId } as Domain, row.id)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n counterparty(@Root() row: Opportunity) {\n return row.counterparty ?? (row.counterpartyId ? getRepository(Counterparty).findOneBy({ id: row.counterpartyId }) : undefined)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n endCustomer(@Root() row: Opportunity) {\n return row.endCustomer ?? (row.endCustomerId ? getRepository(Counterparty).findOneBy({ id: row.endCustomerId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n owner(@Root() row: Opportunity) {\n return row.owner ?? (row.ownerId ? getRepository(User).findOneBy({ id: row.ownerId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Opportunity) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Opportunity) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Opportunity) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import { Domain } from '@things-factory/shell';
|
|
1
|
+
import { Domain, ListParam } from '@things-factory/shell';
|
|
2
2
|
import { EntityManager } from 'typeorm';
|
|
3
3
|
import { Opportunity } from './opportunity';
|
|
4
4
|
import { OpportunityStage } from './opportunity-rules';
|
|
5
5
|
import { NewOpportunity } from './opportunity-type';
|
|
6
|
+
/** What the list finds the opportunities by — a term matched in these, as the list and its stage counts both do. */
|
|
7
|
+
export declare const OPPORTUNITY_SEARCHABLES: string[];
|
|
8
|
+
/**
|
|
9
|
+
* How many opportunities each stage holds, under the list's other conditions — its search and any filter but the
|
|
10
|
+
* stage, so the counts beside the stage chips agree with one another whichever stages are chosen. Counted by the
|
|
11
|
+
* database, grouped by stage; a stage holding none is given as 0, in stage order, each saying whether it is open.
|
|
12
|
+
*/
|
|
13
|
+
export declare function opportunityStageCounts(tx: EntityManager, domain: Domain, params: ListParam): Promise<{
|
|
14
|
+
stage: OpportunityStage;
|
|
15
|
+
count: number;
|
|
16
|
+
open: boolean;
|
|
17
|
+
}[]>;
|
|
6
18
|
export declare function opportunityInDomain(tx: EntityManager, domain: Domain, id: string): Promise<Opportunity>;
|
|
7
19
|
/**
|
|
8
20
|
* Create an opportunity, or change one a person may still change. An open one takes any open stage; a lost one is
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OPPORTUNITY_SEARCHABLES = void 0;
|
|
4
|
+
exports.opportunityStageCounts = opportunityStageCounts;
|
|
3
5
|
exports.opportunityInDomain = opportunityInDomain;
|
|
4
6
|
exports.saveOpportunity = saveOpportunity;
|
|
5
7
|
exports.closeOpportunity = closeOpportunity;
|
|
@@ -12,6 +14,17 @@ const sales_event_1 = require("../sales-event");
|
|
|
12
14
|
const opportunity_1 = require("./opportunity");
|
|
13
15
|
const opportunity_event_1 = require("./opportunity-event");
|
|
14
16
|
const opportunity_rules_1 = require("./opportunity-rules");
|
|
17
|
+
/** What the list finds the opportunities by — a term matched in these, as the list and its stage counts both do. */
|
|
18
|
+
exports.OPPORTUNITY_SEARCHABLES = ['name', 'description'];
|
|
19
|
+
/**
|
|
20
|
+
* How many opportunities each stage holds, under the list's other conditions — its search and any filter but the
|
|
21
|
+
* stage, so the counts beside the stage chips agree with one another whichever stages are chosen. Counted by the
|
|
22
|
+
* database, grouped by stage; a stage holding none is given as 0, in stage order, each saying whether it is open.
|
|
23
|
+
*/
|
|
24
|
+
async function opportunityStageCounts(tx, domain, params) {
|
|
25
|
+
const counted = await (0, shell_1.countListByField)({ domain, params, repository: tx.getRepository(opportunity_1.Opportunity), searchables: exports.OPPORTUNITY_SEARCHABLES, field: 'stage' });
|
|
26
|
+
return Object.values(opportunity_rules_1.OpportunityStage).map(stage => ({ stage, count: counted.get(stage) ?? 0, open: (0, opportunity_rules_1.isOpen)(stage) }));
|
|
27
|
+
}
|
|
15
28
|
async function opportunityInDomain(tx, domain, id) {
|
|
16
29
|
const opportunity = await tx.getRepository(opportunity_1.Opportunity).findOne({ where: { id, domain: { id: domain.id } } });
|
|
17
30
|
if (!opportunity)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opportunity-service.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDomainAssignees } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { currencyDigits, validDay } from '@things-factory/invoice'\nimport { Domain, Refusal, calendarDay } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { recordSalesEvent } from '../sales-event'\nimport { Opportunity } from './opportunity'\nimport { OpportunityEvent } from './opportunity-event'\nimport { isOpen, OpportunityStage, probabilityAfter, stageMove, validMonth, validProbability } from './opportunity-rules'\nimport { NewOpportunity } from './opportunity-type'\n\nexport async function opportunityInDomain(tx: EntityManager, domain: Domain, id: string) {\n const opportunity = await tx.getRepository(Opportunity).findOne({ where: { id, domain: { id: domain.id } } })\n if (!opportunity) throw new Refusal('opportunity-not-found', {}, 'Opportunity not found')\n return opportunity\n}\n\n/**\n * Create an opportunity, or change one a person may still change. An open one takes any open stage; a lost one is\n * opened again by giving it an open stage. Closing is `closeOpportunity`'s.\n */\nexport async function saveOpportunity(tx: EntityManager, domain: Domain, user: any, input: NewOpportunity, id?: string) {\n const old = id ? await opportunityInDomain(tx, domain, id) : undefined\n const stage = input.stage ?? old?.stage ?? OpportunityStage.LEAD\n /* A closed one changes only by being opened again, and only a lost one may be. */\n if (old && !isOpen(old.stage) && !(isOpen(stage) && stageMove(old.stage, stage))) throw new Refusal('opportunity-closed', {}, 'A closed opportunity is not changed')\n if (!isOpen(stage)) throw new Refusal('opportunity-stage-open-required', {}, 'Give an open stage; won and lost are closings')\n\n const name = input.name?.trim()\n if (!name) throw new Refusal('opportunity-name-required', {}, 'Name the opportunity')\n if (!Number.isFinite(input.amount) || input.amount < 0) throw new Refusal('opportunity-amount-invalid', {}, 'The expected amount is zero or more')\n const currency = (input.currency?.trim() || 'KRW').toUpperCase()\n currencyDigits(currency)\n if (!validMonth(input.expectedCloseMonth)) throw new Refusal('opportunity-close-month-invalid', {}, 'Give the expected month as YYYY-MM')\n if (input.probability !== undefined && input.probability !== null && !validProbability(input.probability))\n throw new Refusal('opportunity-probability-invalid', { value: String(input.probability), min: 0, max: 100 }, 'The probability is a whole number from 0 to 100')\n\n const counterparties = tx.getRepository(Counterparty)\n const counterparty = input.counterpartyId ? await counterparties.findOne({ where: { id: input.counterpartyId, domain: { id: domain.id } } }) : null\n if (!counterparty) throw new Refusal('opportunity-counterparty-required', {}, 'Choose the counterparty of this domain')\n const endCustomer = input.endCustomerId ? await counterparties.findOne({ where: { id: input.endCustomerId, domain: { id: domain.id } } }) : null\n if (input.endCustomerId && !endCustomer) throw new Refusal('opportunity-end-customer-invalid', {}, 'Choose the end customer among the counterparties of this domain')\n\n const ownerId = input.ownerId || old?.ownerId || user.id\n /* The people work here may be given to — auth-base's one answer, the same a license's recipient gets. */\n if (ownerId !== user.id && ownerId !== old?.ownerId && !(await getDomainAssignees(domain, tx)).some(one => one.id === ownerId))\n throw new Refusal('opportunity-owner-invalid', {}, 'Put it in the charge of a member of this domain')\n\n const staged = !!old && old.stage !== stage\n /* The probability follows the stage unless the person gave one; with the stage unchanged it stays as it was. */\n const probability = !old || staged ? probabilityAfter(stage, input.probability) : input.probability ?? old.probability\n\n const repository = tx.getRepository(Opportunity)\n const row = await repository.save(\n repository.create({\n ...(old ? { id: old.id, creator: old.creatorId ? ({ id: old.creatorId } as any) : undefined } : { creator: user }),\n domain,\n name,\n counterparty,\n endCustomer: endCustomer ?? null,\n amount: input.amount,\n currency,\n expectedCloseMonth: input.expectedCloseMonth,\n stage,\n probability,\n owner: { id: ownerId } as any,\n /* A lost opportunity opened again is no longer closed. */\n closedOn: null,\n lostReason: null,\n description: input.description?.trim() || null,\n updater: user\n })\n )\n const saved = await opportunityInDomain(tx, domain, row.id)\n await recordOpportunityEvent(tx, domain, user, saved, !old ? 'CREATED' : staged ? 'STAGED' : 'UPDATED', staged ? old.stage : undefined)\n return saved\n}\n\n/**\n * Close an open opportunity — lost, with the reason, by a person; won by the application that turned it into its own\n * record (Office's contract, ADR-0138 decision 8). The day is the domain's today unless one is given.\n */\nexport async function closeOpportunity(\n tx: EntityManager,\n domain: Domain,\n user: any,\n id: string,\n outcome: OpportunityStage.WON | OpportunityStage.LOST,\n { closedOn, reason }: { closedOn?: string; reason?: string } = {}\n) {\n const opportunity = await opportunityInDomain(tx, domain, id)\n if (!stageMove(opportunity.stage, outcome)) throw new Refusal('opportunity-stage-move-invalid', {}, 'The opportunity cannot move to that stage')\n if (outcome === OpportunityStage.LOST && !reason?.trim()) throw new Refusal('opportunity-reason-required', {}, 'Say why it was lost')\n const day = closedOn || calendarDay(domain.timezone)\n if (!validDay(day)) throw new Refusal('opportunity-closed-on-invalid', {}, 'Give the closing day as YYYY-MM-DD')\n\n const from = opportunity.stage\n await tx.getRepository(Opportunity).update(\n { id: opportunity.id },\n { stage: outcome, probability: probabilityAfter(outcome), closedOn: day, lostReason: outcome === OpportunityStage.LOST ? reason!.trim() : null, updater: user }\n )\n const closed = await opportunityInDomain(tx, domain, id)\n await recordOpportunityEvent(tx, domain, user, closed, outcome, from, outcome === OpportunityStage.LOST ? closed.lostReason : undefined)\n return closed\n}\n\n/**\n * A won opportunity opened again, because the record its win became was undone - an application's contract link taken\n * back (the user, 2026-10-30: 「계약 연결을 취소할 수 있어야지」). Only the application that closed it as won undoes it; a\n * person does not move a won one. It goes back to the open stage it was won from, with that stage's probability.\n */\nexport async function reopenWonOpportunity(tx: EntityManager, domain: Domain, user: any, id: string, reason: string) {\n const opportunity = await opportunityInDomain(tx, domain, id)\n if (opportunity.stage !== OpportunityStage.WON) throw new Refusal('opportunity-not-won', {}, 'Only a won opportunity is opened again this way')\n if (!reason?.trim()) throw new Refusal('opportunity-reopen-reason-required', {}, 'Say why it is opened again')\n const won = await tx.getRepository(OpportunityEvent).findOne({ where: { opportunity: { id }, action: OpportunityStage.WON } as any, order: { createdAt: 'DESC' } as any })\n const stage = won?.fromStage && isOpen(won.fromStage) ? won.fromStage : OpportunityStage.NEGOTIATION\n await tx.getRepository(Opportunity).update({ id }, { stage, probability: probabilityAfter(stage), closedOn: null, lostReason: null, updater: user } as any)\n const reopened = await opportunityInDomain(tx, domain, id)\n await recordOpportunityEvent(tx, domain, user, reopened, 'REOPENED', OpportunityStage.WON, reason.trim())\n return reopened\n}\n\nfunction recordOpportunityEvent(tx: EntityManager, domain: Domain, user: any, opportunity: Opportunity, action: string, fromStage?: OpportunityStage, reason?: string) {\n return recordSalesEvent(\n tx,\n OpportunityEvent,\n { opportunity, fromStage: fromStage ?? null, stage: opportunity.stage, probability: opportunity.probability },\n { domain, user, action, reason, snapshot: snapshotOf(opportunity) }\n )\n}\n\n/** The opportunity as it stood. */\nfunction snapshotOf(opportunity: Opportunity) {\n const keys = ['id', 'name', 'counterpartyId', 'endCustomerId', 'amount', 'currency', 'expectedCloseMonth', 'stage', 'probability', 'ownerId', 'closedOn', 'lostReason', 'description']\n return Object.fromEntries(keys.map(key => [key, opportunity[key] ?? null]))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"opportunity-service.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDomainAssignees } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { currencyDigits, validDay } from '@things-factory/invoice'\nimport { calendarDay, countListByField, Domain, ListParam, Refusal } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { recordSalesEvent } from '../sales-event'\nimport { Opportunity } from './opportunity'\nimport { OpportunityEvent } from './opportunity-event'\nimport { isOpen, OpportunityStage, probabilityAfter, stageMove, validMonth, validProbability } from './opportunity-rules'\nimport { NewOpportunity } from './opportunity-type'\n\n/** What the list finds the opportunities by — a term matched in these, as the list and its stage counts both do. */\nexport const OPPORTUNITY_SEARCHABLES = ['name', 'description']\n\n/**\n * How many opportunities each stage holds, under the list's other conditions — its search and any filter but the\n * stage, so the counts beside the stage chips agree with one another whichever stages are chosen. Counted by the\n * database, grouped by stage; a stage holding none is given as 0, in stage order, each saying whether it is open.\n */\nexport async function opportunityStageCounts(tx: EntityManager, domain: Domain, params: ListParam) {\n const counted = await countListByField({ domain, params, repository: tx.getRepository(Opportunity), searchables: OPPORTUNITY_SEARCHABLES, field: 'stage' })\n return Object.values(OpportunityStage).map(stage => ({ stage, count: counted.get(stage) ?? 0, open: isOpen(stage) }))\n}\n\nexport async function opportunityInDomain(tx: EntityManager, domain: Domain, id: string) {\n const opportunity = await tx.getRepository(Opportunity).findOne({ where: { id, domain: { id: domain.id } } })\n if (!opportunity) throw new Refusal('opportunity-not-found', {}, 'Opportunity not found')\n return opportunity\n}\n\n/**\n * Create an opportunity, or change one a person may still change. An open one takes any open stage; a lost one is\n * opened again by giving it an open stage. Closing is `closeOpportunity`'s.\n */\nexport async function saveOpportunity(tx: EntityManager, domain: Domain, user: any, input: NewOpportunity, id?: string) {\n const old = id ? await opportunityInDomain(tx, domain, id) : undefined\n const stage = input.stage ?? old?.stage ?? OpportunityStage.LEAD\n /* A closed one changes only by being opened again, and only a lost one may be. */\n if (old && !isOpen(old.stage) && !(isOpen(stage) && stageMove(old.stage, stage))) throw new Refusal('opportunity-closed', {}, 'A closed opportunity is not changed')\n if (!isOpen(stage)) throw new Refusal('opportunity-stage-open-required', {}, 'Give an open stage; won and lost are closings')\n\n const name = input.name?.trim()\n if (!name) throw new Refusal('opportunity-name-required', {}, 'Name the opportunity')\n if (!Number.isFinite(input.amount) || input.amount < 0) throw new Refusal('opportunity-amount-invalid', {}, 'The expected amount is zero or more')\n const currency = (input.currency?.trim() || 'KRW').toUpperCase()\n currencyDigits(currency)\n if (!validMonth(input.expectedCloseMonth)) throw new Refusal('opportunity-close-month-invalid', {}, 'Give the expected month as YYYY-MM')\n if (input.probability !== undefined && input.probability !== null && !validProbability(input.probability))\n throw new Refusal('opportunity-probability-invalid', { value: String(input.probability), min: 0, max: 100 }, 'The probability is a whole number from 0 to 100')\n\n const counterparties = tx.getRepository(Counterparty)\n const counterparty = input.counterpartyId ? await counterparties.findOne({ where: { id: input.counterpartyId, domain: { id: domain.id } } }) : null\n if (!counterparty) throw new Refusal('opportunity-counterparty-required', {}, 'Choose the counterparty of this domain')\n const endCustomer = input.endCustomerId ? await counterparties.findOne({ where: { id: input.endCustomerId, domain: { id: domain.id } } }) : null\n if (input.endCustomerId && !endCustomer) throw new Refusal('opportunity-end-customer-invalid', {}, 'Choose the end customer among the counterparties of this domain')\n\n const ownerId = input.ownerId || old?.ownerId || user.id\n /* The people work here may be given to — auth-base's one answer, the same a license's recipient gets. */\n if (ownerId !== user.id && ownerId !== old?.ownerId && !(await getDomainAssignees(domain, tx)).some(one => one.id === ownerId))\n throw new Refusal('opportunity-owner-invalid', {}, 'Put it in the charge of a member of this domain')\n\n const staged = !!old && old.stage !== stage\n /* The probability follows the stage unless the person gave one; with the stage unchanged it stays as it was. */\n const probability = !old || staged ? probabilityAfter(stage, input.probability) : input.probability ?? old.probability\n\n const repository = tx.getRepository(Opportunity)\n const row = await repository.save(\n repository.create({\n ...(old ? { id: old.id, creator: old.creatorId ? ({ id: old.creatorId } as any) : undefined } : { creator: user }),\n domain,\n name,\n counterparty,\n endCustomer: endCustomer ?? null,\n amount: input.amount,\n currency,\n expectedCloseMonth: input.expectedCloseMonth,\n stage,\n probability,\n owner: { id: ownerId } as any,\n /* A lost opportunity opened again is no longer closed. */\n closedOn: null,\n lostReason: null,\n description: input.description?.trim() || null,\n updater: user\n })\n )\n const saved = await opportunityInDomain(tx, domain, row.id)\n await recordOpportunityEvent(tx, domain, user, saved, !old ? 'CREATED' : staged ? 'STAGED' : 'UPDATED', staged ? old.stage : undefined)\n return saved\n}\n\n/**\n * Close an open opportunity — lost, with the reason, by a person; won by the application that turned it into its own\n * record (Office's contract, ADR-0138 decision 8). The day is the domain's today unless one is given.\n */\nexport async function closeOpportunity(\n tx: EntityManager,\n domain: Domain,\n user: any,\n id: string,\n outcome: OpportunityStage.WON | OpportunityStage.LOST,\n { closedOn, reason }: { closedOn?: string; reason?: string } = {}\n) {\n const opportunity = await opportunityInDomain(tx, domain, id)\n if (!stageMove(opportunity.stage, outcome)) throw new Refusal('opportunity-stage-move-invalid', {}, 'The opportunity cannot move to that stage')\n if (outcome === OpportunityStage.LOST && !reason?.trim()) throw new Refusal('opportunity-reason-required', {}, 'Say why it was lost')\n const day = closedOn || calendarDay(domain.timezone)\n if (!validDay(day)) throw new Refusal('opportunity-closed-on-invalid', {}, 'Give the closing day as YYYY-MM-DD')\n\n const from = opportunity.stage\n await tx.getRepository(Opportunity).update(\n { id: opportunity.id },\n { stage: outcome, probability: probabilityAfter(outcome), closedOn: day, lostReason: outcome === OpportunityStage.LOST ? reason!.trim() : null, updater: user }\n )\n const closed = await opportunityInDomain(tx, domain, id)\n await recordOpportunityEvent(tx, domain, user, closed, outcome, from, outcome === OpportunityStage.LOST ? closed.lostReason : undefined)\n return closed\n}\n\n/**\n * A won opportunity opened again, because the record its win became was undone - an application's contract link taken\n * back (the user, 2026-10-30: 「계약 연결을 취소할 수 있어야지」). Only the application that closed it as won undoes it; a\n * person does not move a won one. It goes back to the open stage it was won from, with that stage's probability.\n */\nexport async function reopenWonOpportunity(tx: EntityManager, domain: Domain, user: any, id: string, reason: string) {\n const opportunity = await opportunityInDomain(tx, domain, id)\n if (opportunity.stage !== OpportunityStage.WON) throw new Refusal('opportunity-not-won', {}, 'Only a won opportunity is opened again this way')\n if (!reason?.trim()) throw new Refusal('opportunity-reopen-reason-required', {}, 'Say why it is opened again')\n const won = await tx.getRepository(OpportunityEvent).findOne({ where: { opportunity: { id }, action: OpportunityStage.WON } as any, order: { createdAt: 'DESC' } as any })\n const stage = won?.fromStage && isOpen(won.fromStage) ? won.fromStage : OpportunityStage.NEGOTIATION\n await tx.getRepository(Opportunity).update({ id }, { stage, probability: probabilityAfter(stage), closedOn: null, lostReason: null, updater: user } as any)\n const reopened = await opportunityInDomain(tx, domain, id)\n await recordOpportunityEvent(tx, domain, user, reopened, 'REOPENED', OpportunityStage.WON, reason.trim())\n return reopened\n}\n\nfunction recordOpportunityEvent(tx: EntityManager, domain: Domain, user: any, opportunity: Opportunity, action: string, fromStage?: OpportunityStage, reason?: string) {\n return recordSalesEvent(\n tx,\n OpportunityEvent,\n { opportunity, fromStage: fromStage ?? null, stage: opportunity.stage, probability: opportunity.probability },\n { domain, user, action, reason, snapshot: snapshotOf(opportunity) }\n )\n}\n\n/** The opportunity as it stood. */\nfunction snapshotOf(opportunity: Opportunity) {\n const keys = ['id', 'name', 'counterpartyId', 'endCustomerId', 'amount', 'currency', 'expectedCloseMonth', 'stage', 'probability', 'ownerId', 'closedOn', 'lostReason', 'description']\n return Object.fromEntries(keys.map(key => [key, opportunity[key] ?? null]))\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OpportunityStageDefault = exports.OpportunityList = exports.NewOpportunity = void 0;
|
|
3
|
+
exports.OpportunityStageCount = exports.OpportunityStageDefault = exports.OpportunityList = exports.NewOpportunity = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
6
|
const opportunity_1 = require("./opportunity");
|
|
@@ -83,4 +83,22 @@ tslib_1.__decorate([
|
|
|
83
83
|
exports.OpportunityStageDefault = OpportunityStageDefault = tslib_1.__decorate([
|
|
84
84
|
(0, type_graphql_1.ObjectType)({ description: 'A stage and the probability it starts an opportunity at' })
|
|
85
85
|
], OpportunityStageDefault);
|
|
86
|
+
let OpportunityStageCount = class OpportunityStageCount {
|
|
87
|
+
};
|
|
88
|
+
exports.OpportunityStageCount = OpportunityStageCount;
|
|
89
|
+
tslib_1.__decorate([
|
|
90
|
+
(0, type_graphql_1.Field)(type => opportunity_rules_1.OpportunityStage, { description: 'The stage' }),
|
|
91
|
+
tslib_1.__metadata("design:type", String)
|
|
92
|
+
], OpportunityStageCount.prototype, "stage", void 0);
|
|
93
|
+
tslib_1.__decorate([
|
|
94
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'Opportunities in the stage under the list conditions other than the stage' }),
|
|
95
|
+
tslib_1.__metadata("design:type", Number)
|
|
96
|
+
], OpportunityStageCount.prototype, "count", void 0);
|
|
97
|
+
tslib_1.__decorate([
|
|
98
|
+
(0, type_graphql_1.Field)({ description: 'Whether the stage is open; the list starts with the open stages chosen, won and lost left out' }),
|
|
99
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
100
|
+
], OpportunityStageCount.prototype, "open", void 0);
|
|
101
|
+
exports.OpportunityStageCount = OpportunityStageCount = tslib_1.__decorate([
|
|
102
|
+
(0, type_graphql_1.ObjectType)({ description: 'A stage and how many opportunities the list holds in it' })
|
|
103
|
+
], OpportunityStageCount);
|
|
86
104
|
//# sourceMappingURL=opportunity-type.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opportunity-type.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAuE;AAEvE,+CAA2C;AAC3C,2DAAsD;AAG/C,IAAM,cAAc,GAApB,MAAM,cAAc;CAW1B,CAAA;AAXY,wCAAc;AACiC;IAAzD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;;4CAAa;AACW;IAAhF,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;sDAAuB;AACM;IAA5G,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC;;qDAAuB;AAC9D;IAApE,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAK,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;8CAAe;AACD;IAAjF,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;gDAAkB;AAC5B;IAAtE,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;;0DAA2B;AACiC;IAAjI,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oCAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;6CAAyB;AACyB;IAAlL,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0HAA0H,EAAE,CAAC;;mDAAqB;AAC1G;IAA5F,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;+CAAiB;AAC5D;IAAhD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;;mDAAqB;yBAV1D,cAAc;IAD1B,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC;GAC3E,cAAc,CAW1B;AAGM,IAAM,eAAe,GAArB,MAAM,eAAe;CAG3B,CAAA;AAHY,0CAAe;AACkD;IAA3E,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,yBAAW,CAAC,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;;8CAAqB;AACH;IAA5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;8CAAc;0BAF/F,eAAe;IAD3B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;GAC1C,eAAe,CAG3B;AAGM,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;CAInC,CAAA;AAJY,0DAAuB;AAC6B;IAA9D,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oCAAgB,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;;sDAAwB;AAC+B;IAApH,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,8EAA8E,EAAE,CAAC;;4DAAoB;AAC7B;IAA1G,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,iFAAiF,EAAE,CAAC;;qDAAc;kCAH7G,uBAAuB;IADnC,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;GAC1E,uBAAuB,CAInC","sourcesContent":["import { Field, Float, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Opportunity } from './opportunity'\nimport { OpportunityStage } from './opportunity-rules'\n\n@InputType({ description: 'An opportunity to create, or what a person changes on one' })\nexport class NewOpportunity {\n @Field({ description: 'What the opportunity is called' }) name: string\n @Field({ description: 'Id of the counterparty the domain would contract with' }) counterpartyId: string\n @Field({ nullable: true, description: 'Id of the end customer, when the work comes through another party' }) endCustomerId?: string\n @Field(type => Float, { description: 'Expected amount before tax' }) amount: number\n @Field({ nullable: true, description: 'ISO 4217 currency code, KRW when empty' }) currency?: string\n @Field({ description: 'Month the domain expects to win it, YYYY-MM' }) expectedCloseMonth: string\n @Field(type => OpportunityStage, { nullable: true, description: 'An open stage; a new opportunity starts as a lead when empty' }) stage?: OpportunityStage\n @Field(type => Float, { nullable: true, description: 'Chance of winning in percent, a whole number from 0 to 100 - another is refused naming it; the stage gives it when empty' }) probability?: number\n @Field({ nullable: true, description: 'Id of the person in charge; the caller when empty' }) ownerId?: string\n @Field({ nullable: true, description: 'Notes' }) description?: string\n}\n\n@ObjectType({ description: 'A page of opportunities' })\nexport class OpportunityList {\n @Field(type => [Opportunity], { description: 'Opportunities on the page' }) items: Opportunity[]\n @Field(type => Int, { description: 'Number of opportunities matching the list parameters' }) total: number\n}\n\n@ObjectType({ description: 'A stage and the probability it starts an opportunity at' })\nexport class OpportunityStageDefault {\n @Field(type => OpportunityStage, { description: 'The stage' }) stage: OpportunityStage\n @Field(type => Int, { description: 'The probability the stage gives, in percent — a judgement, not a measurement' }) probability: number\n @Field({ description: 'Whether a person moves an opportunity through this stage; won and lost close it' }) open: boolean\n}\n"]}
|
|
1
|
+
{"version":3,"file":"opportunity-type.js","sourceRoot":"","sources":["../../../server/service/opportunity/opportunity-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, Float, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Opportunity } from './opportunity'\nimport { OpportunityStage } from './opportunity-rules'\n\n@InputType({ description: 'An opportunity to create, or what a person changes on one' })\nexport class NewOpportunity {\n @Field({ description: 'What the opportunity is called' }) name: string\n @Field({ description: 'Id of the counterparty the domain would contract with' }) counterpartyId: string\n @Field({ nullable: true, description: 'Id of the end customer, when the work comes through another party' }) endCustomerId?: string\n @Field(type => Float, { description: 'Expected amount before tax' }) amount: number\n @Field({ nullable: true, description: 'ISO 4217 currency code, KRW when empty' }) currency?: string\n @Field({ description: 'Month the domain expects to win it, YYYY-MM' }) expectedCloseMonth: string\n @Field(type => OpportunityStage, { nullable: true, description: 'An open stage; a new opportunity starts as a lead when empty' }) stage?: OpportunityStage\n @Field(type => Float, { nullable: true, description: 'Chance of winning in percent, a whole number from 0 to 100 - another is refused naming it; the stage gives it when empty' }) probability?: number\n @Field({ nullable: true, description: 'Id of the person in charge; the caller when empty' }) ownerId?: string\n @Field({ nullable: true, description: 'Notes' }) description?: string\n}\n\n@ObjectType({ description: 'A page of opportunities' })\nexport class OpportunityList {\n @Field(type => [Opportunity], { description: 'Opportunities on the page' }) items: Opportunity[]\n @Field(type => Int, { description: 'Number of opportunities matching the list parameters' }) total: number\n}\n\n@ObjectType({ description: 'A stage and the probability it starts an opportunity at' })\nexport class OpportunityStageDefault {\n @Field(type => OpportunityStage, { description: 'The stage' }) stage: OpportunityStage\n @Field(type => Int, { description: 'The probability the stage gives, in percent — a judgement, not a measurement' }) probability: number\n @Field({ description: 'Whether a person moves an opportunity through this stage; won and lost close it' }) open: boolean\n}\n\n@ObjectType({ description: 'A stage and how many opportunities the list holds in it' })\nexport class OpportunityStageCount {\n @Field(type => OpportunityStage, { description: 'The stage' }) stage: OpportunityStage\n @Field(type => Int, { description: 'Opportunities in the stage under the list conditions other than the stage' }) count: number\n @Field({ description: 'Whether the stage is open; the list starts with the open stages chosen, won and lost left out' }) open: boolean\n}\n"]}
|
|
@@ -9,6 +9,10 @@ import { QuotationList } from './quotation-type';
|
|
|
9
9
|
export declare class QuotationQuery {
|
|
10
10
|
quotation(id: string, context: ResolverContext): Promise<Quotation>;
|
|
11
11
|
quotations(params: ListParam, context: ResolverContext): Promise<QuotationList>;
|
|
12
|
+
quotationStatusCounts(params: ListParam, context: ResolverContext): Promise<{
|
|
13
|
+
value: import("./quotation-status").QuotationStatus;
|
|
14
|
+
count: number;
|
|
15
|
+
}[]>;
|
|
12
16
|
quotationVersions(number: string, context: ResolverContext): Promise<Quotation[]>;
|
|
13
17
|
quotationEvents(quotationId: string, context: ResolverContext): Promise<QuotationEvent[]>;
|
|
14
18
|
quotationDocumentFile(id: string, format: DocumentFileFormat, language: string, context: ResolverContext): Promise<DocumentFile>;
|
|
@@ -21,11 +21,14 @@ let QuotationQuery = class QuotationQuery {
|
|
|
21
21
|
domain: context.state.domain,
|
|
22
22
|
params,
|
|
23
23
|
repository: (0, shell_1.getRepository)(quotation_1.Quotation),
|
|
24
|
-
searchables:
|
|
24
|
+
searchables: quotation_service_1.QUOTATION_SEARCHABLES
|
|
25
25
|
});
|
|
26
26
|
const [items, total] = await query.getManyAndCount();
|
|
27
27
|
return { items, total };
|
|
28
28
|
}
|
|
29
|
+
quotationStatusCounts(params, context) {
|
|
30
|
+
return (0, quotation_service_1.quotationStatusCounts)((0, shell_1.getRepository)(quotation_1.Quotation).manager, context.state.domain, params);
|
|
31
|
+
}
|
|
29
32
|
quotationVersions(number, context) {
|
|
30
33
|
return (0, shell_1.getRepository)(quotation_1.Quotation).find({ where: { domain: { id: context.state.domain.id }, number }, order: { version: 'ASC' } });
|
|
31
34
|
}
|
|
@@ -87,6 +90,15 @@ tslib_1.__decorate([
|
|
|
87
90
|
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
88
91
|
tslib_1.__metadata("design:returntype", Promise)
|
|
89
92
|
], QuotationQuery.prototype, "quotations", null);
|
|
93
|
+
tslib_1.__decorate([
|
|
94
|
+
(0, type_graphql_1.Directive)('@privilege(category: "sales", privilege: "query", domainOwnerGranted: true)'),
|
|
95
|
+
(0, type_graphql_1.Query)(type => [shell_1.ListValueCount], { description: 'To count the quotations in each status, under the list conditions other than the status, every status in order' }),
|
|
96
|
+
tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
|
|
97
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
98
|
+
tslib_1.__metadata("design:type", Function),
|
|
99
|
+
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
100
|
+
tslib_1.__metadata("design:returntype", void 0)
|
|
101
|
+
], QuotationQuery.prototype, "quotationStatusCounts", null);
|
|
90
102
|
tslib_1.__decorate([
|
|
91
103
|
(0, type_graphql_1.Directive)('@privilege(category: "sales", privilege: "query", domainOwnerGranted: true)'),
|
|
92
104
|
(0, type_graphql_1.Query)(type => [quotation_1.Quotation], { description: 'To fetch every version of a quotation number, oldest first' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotation-query.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { DocumentFile, DocumentFileFormat, documentFileOf, DocumentTaxGroup, lineSupplyOf, printTranslator, taxGroupsOf } from '@things-factory/invoice'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Float, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { Opportunity } from '../opportunity/opportunity'\nimport { Quotation } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\nimport { quotationInDomain } from './quotation-service'\nimport { QuotationList } from './quotation-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Quotation)\nexport class QuotationQuery {\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Quotation, { nullable: true, description: 'To fetch a quotation' })\n quotation(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Quotation).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => QuotationList, { description: 'To fetch a page of quotations' })\n async quotations(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<QuotationList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Quotation),\n searchables: ['number', 'supplierName', 'buyerName', 'notes']\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [Quotation], { description: 'To fetch every version of a quotation number, oldest first' })\n quotationVersions(@Arg('number') number: string, @Ctx() context: ResolverContext) {\n return getRepository(Quotation).find({ where: { domain: { id: context.state.domain.id }, number }, order: { version: 'ASC' } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [QuotationEvent], { description: 'To fetch what happened to a quotation, oldest first' })\n quotationEvents(@Arg('quotationId') quotationId: string, @Ctx() context: ResolverContext) {\n return getRepository(QuotationEvent).find({ where: { domain: { id: context.state.domain.id }, quotation: { id: quotationId } }, order: { createdAt: 'ASC' } })\n }\n\n /* The quotation as its buyer reads it (ADR-0170 decision 10) - the page to preview and print, or the PDF to download. */\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => DocumentFile, { description: 'Prints a quotation as an HTML page or a PDF, in Korean or English - its own language when none is named' })\n async quotationDocumentFile(\n @Arg('id') id: string,\n @Arg('format', type => DocumentFileFormat, { description: 'An HTML page or a PDF' }) format: DocumentFileFormat,\n @Arg('language', { nullable: true, description: 'The language of the print, ko or en - the quotation\\'s own when empty' }) language: string,\n @Ctx() context: ResolverContext\n ): Promise<DocumentFile> {\n const quotation = await quotationInDomain(getRepository(Quotation).manager, context.state.domain, id)\n const print = printTranslator(context, language || quotation.language)\n return documentFileOf(quotation, format, {\n ...print,\n title: print.t('title.quotation-print'),\n numberLabel: 'field.quotation-number',\n terms: [\n { label: print.t('field.quotation-version'), value: `v${quotation.version}` },\n { label: print.t('field.quotation-valid-until'), value: quotation.validUntil }\n ]\n })\n }\n\n @FieldResolver(type => [QuotationItem])\n async items(@Root() quotation: Quotation) {\n return quotation.items ?? getRepository(QuotationItem).find({ where: { quotation: { id: quotation.id } }, order: { position: 'ASC' } })\n }\n\n @FieldResolver(type => [DocumentTaxGroup])\n async taxGroups(@Root() quotation: Quotation) {\n return taxGroupsOf({ ...quotation, items: await this.items(quotation) })\n }\n\n @FieldResolver(type => Float, { description: \"The lines' supply before the discount of the whole document\" })\n async lineSupplyAmount(@Root() quotation: Quotation) {\n return lineSupplyOf({ ...quotation, items: await this.items(quotation) })\n }\n\n @FieldResolver(type => Opportunity, { nullable: true })\n opportunity(@Root() row: Quotation) {\n return row.opportunity ?? (row.opportunityId ? getRepository(Opportunity).findOneBy({ id: row.opportunityId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Quotation) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Quotation) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Quotation) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"quotation-query.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { DocumentFile, DocumentFileFormat, documentFileOf, DocumentTaxGroup, lineSupplyOf, printTranslator, taxGroupsOf } from '@things-factory/invoice'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, ListValueCount, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Float, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { Opportunity } from '../opportunity/opportunity'\nimport { Quotation } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\nimport { QUOTATION_SEARCHABLES, quotationInDomain, quotationStatusCounts } from './quotation-service'\nimport { QuotationList } from './quotation-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Quotation)\nexport class QuotationQuery {\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Quotation, { nullable: true, description: 'To fetch a quotation' })\n quotation(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Quotation).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => QuotationList, { description: 'To fetch a page of quotations' })\n async quotations(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<QuotationList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Quotation),\n searchables: QUOTATION_SEARCHABLES\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [ListValueCount], { description: 'To count the quotations in each status, under the list conditions other than the status, every status in order' })\n quotationStatusCounts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext) {\n return quotationStatusCounts(getRepository(Quotation).manager, context.state.domain, params)\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [Quotation], { description: 'To fetch every version of a quotation number, oldest first' })\n quotationVersions(@Arg('number') number: string, @Ctx() context: ResolverContext) {\n return getRepository(Quotation).find({ where: { domain: { id: context.state.domain.id }, number }, order: { version: 'ASC' } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [QuotationEvent], { description: 'To fetch what happened to a quotation, oldest first' })\n quotationEvents(@Arg('quotationId') quotationId: string, @Ctx() context: ResolverContext) {\n return getRepository(QuotationEvent).find({ where: { domain: { id: context.state.domain.id }, quotation: { id: quotationId } }, order: { createdAt: 'ASC' } })\n }\n\n /* The quotation as its buyer reads it (ADR-0170 decision 10) - the page to preview and print, or the PDF to download. */\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => DocumentFile, { description: 'Prints a quotation as an HTML page or a PDF, in Korean or English - its own language when none is named' })\n async quotationDocumentFile(\n @Arg('id') id: string,\n @Arg('format', type => DocumentFileFormat, { description: 'An HTML page or a PDF' }) format: DocumentFileFormat,\n @Arg('language', { nullable: true, description: 'The language of the print, ko or en - the quotation\\'s own when empty' }) language: string,\n @Ctx() context: ResolverContext\n ): Promise<DocumentFile> {\n const quotation = await quotationInDomain(getRepository(Quotation).manager, context.state.domain, id)\n const print = printTranslator(context, language || quotation.language)\n return documentFileOf(quotation, format, {\n ...print,\n title: print.t('title.quotation-print'),\n numberLabel: 'field.quotation-number',\n terms: [\n { label: print.t('field.quotation-version'), value: `v${quotation.version}` },\n { label: print.t('field.quotation-valid-until'), value: quotation.validUntil }\n ]\n })\n }\n\n @FieldResolver(type => [QuotationItem])\n async items(@Root() quotation: Quotation) {\n return quotation.items ?? getRepository(QuotationItem).find({ where: { quotation: { id: quotation.id } }, order: { position: 'ASC' } })\n }\n\n @FieldResolver(type => [DocumentTaxGroup])\n async taxGroups(@Root() quotation: Quotation) {\n return taxGroupsOf({ ...quotation, items: await this.items(quotation) })\n }\n\n @FieldResolver(type => Float, { description: \"The lines' supply before the discount of the whole document\" })\n async lineSupplyAmount(@Root() quotation: Quotation) {\n return lineSupplyOf({ ...quotation, items: await this.items(quotation) })\n }\n\n @FieldResolver(type => Opportunity, { nullable: true })\n opportunity(@Root() row: Quotation) {\n return row.opportunity ?? (row.opportunityId ? getRepository(Opportunity).findOneBy({ id: row.opportunityId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Quotation) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Quotation) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Quotation) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Domain } from '@things-factory/shell';
|
|
1
|
+
import { Domain, ListParam } from '@things-factory/shell';
|
|
2
2
|
import { EntityManager } from 'typeorm';
|
|
3
|
-
import { Quotation } from './quotation';
|
|
3
|
+
import { Quotation, QuotationStatus } from './quotation';
|
|
4
4
|
import { QuotationAction } from './quotation-rules';
|
|
5
5
|
import { NewQuotation } from './quotation-type';
|
|
6
6
|
export declare function quotationInDomain(tx: EntityManager, domain: Domain, id: string): Promise<Quotation>;
|
|
@@ -24,3 +24,10 @@ export declare function snapshotOf(quotation: Quotation): {
|
|
|
24
24
|
};
|
|
25
25
|
/** The quotations that answer an opportunity, every version, by number then version. */
|
|
26
26
|
export declare function quotationsOfOpportunity(tx: EntityManager | undefined, domain: Domain, opportunityId: string): Promise<Quotation[]>;
|
|
27
|
+
/** What the quotation list finds quotations by, and its status counts alike. */
|
|
28
|
+
export declare const QUOTATION_SEARCHABLES: string[];
|
|
29
|
+
/** How many quotations each status chip of the list would show, under the list's other conditions, in status order. */
|
|
30
|
+
export declare function quotationStatusCounts(tx: EntityManager, domain: Domain, params: ListParam): Promise<{
|
|
31
|
+
value: QuotationStatus;
|
|
32
|
+
count: number;
|
|
33
|
+
}[]>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QUOTATION_SEARCHABLES = void 0;
|
|
3
4
|
exports.quotationInDomain = quotationInDomain;
|
|
4
5
|
exports.saveQuotation = saveQuotation;
|
|
5
6
|
exports.transitionQuotation = transitionQuotation;
|
|
6
7
|
exports.reviseQuotation = reviseQuotation;
|
|
7
8
|
exports.snapshotOf = snapshotOf;
|
|
8
9
|
exports.quotationsOfOpportunity = quotationsOfOpportunity;
|
|
10
|
+
exports.quotationStatusCounts = quotationStatusCounts;
|
|
9
11
|
const invoice_1 = require("@things-factory/invoice");
|
|
10
12
|
const shell_1 = require("@things-factory/shell");
|
|
11
13
|
const opportunity_1 = require("../opportunity/opportunity");
|
|
@@ -125,4 +127,11 @@ function quotationsOfOpportunity(tx, domain, opportunityId) {
|
|
|
125
127
|
const repository = tx ? tx.getRepository(quotation_1.Quotation) : (0, shell_1.getRepository)(quotation_1.Quotation);
|
|
126
128
|
return repository.find({ where: { domain: { id: domain.id }, opportunity: { id: opportunityId } }, order: { number: 'ASC', version: 'ASC' } });
|
|
127
129
|
}
|
|
130
|
+
/** What the quotation list finds quotations by, and its status counts alike. */
|
|
131
|
+
exports.QUOTATION_SEARCHABLES = ['number', 'supplierName', 'buyerName', 'notes'];
|
|
132
|
+
/** How many quotations each status chip of the list would show, under the list's other conditions, in status order. */
|
|
133
|
+
async function quotationStatusCounts(tx, domain, params) {
|
|
134
|
+
const counted = await (0, shell_1.countListByField)({ domain, params, repository: tx.getRepository(quotation_1.Quotation), searchables: exports.QUOTATION_SEARCHABLES, field: 'status' });
|
|
135
|
+
return Object.values(quotation_1.QuotationStatus).map(value => ({ value, count: counted.get(value) ?? 0 }));
|
|
136
|
+
}
|
|
128
137
|
//# sourceMappingURL=quotation-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotation-service.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { calculateDocument, currencyDigits, HEADER_KEYS, lockDocumentDomain, normalizedHeader, TAX_ROUNDING, validDay } from '@things-factory/invoice'\nimport { Domain, getRepository, Refusal, calendarDay } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { Opportunity } from '../opportunity/opportunity'\nimport { recordSalesEvent } from '../sales-event'\nimport { Quotation, QuotationStatus } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\nimport { QuotationAction, quotationTransition, revisable } from './quotation-rules'\nimport { NewQuotation } from './quotation-type'\n\nexport async function quotationInDomain(tx: EntityManager, domain: Domain, id: string) {\n const quotation = await tx.getRepository(Quotation).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] })\n if (!quotation) throw new Refusal('quotation-not-found', {}, 'Quotation not found')\n quotation.items.sort((a, b) => a.position - b.position)\n return quotation\n}\n\nexport async function saveQuotation(tx: EntityManager, domain: Domain, user: any, input: NewQuotation, id?: string) {\n await lockDocumentDomain(tx, domain)\n const old = id ? await quotationInDomain(tx, domain, id) : undefined\n if (old && old.status !== QuotationStatus.DRAFT) throw new Refusal('quotation-draft-required', {}, 'Revise a sent quotation to change it')\n\n const header = normalizedHeader(input)\n if (!validDay(input.validUntil)) throw new Refusal('quotation-valid-until', {}, 'A valid last day is required')\n /* A revision answers the quotation it revises: it keeps the number. */\n if (old && old.version > 1 && header.number !== old.number) throw new Refusal('quotation-revision-number', {}, 'A revision keeps its quotation number')\n\n const repository = tx.getRepository(Quotation)\n const version = old?.version ?? 1\n const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } })\n if (duplicate && duplicate.id !== id) throw new Refusal('quotation-number-duplicate', {}, 'Quotation number already exists')\n\n const opportunity = input.opportunityId ? await tx.getRepository(Opportunity).findOne({ where: { id: input.opportunityId, domain: { id: domain.id } } }) : null\n if (input.opportunityId && !opportunity) throw new Refusal('quotation-opportunity-invalid', {}, 'Choose an opportunity of this domain')\n\n const { items, discountAmount, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency, false, input.discountAmount)\n const quotation = await repository.save(\n repository.create({ ...old, ...header, version, validUntil: input.validUntil, opportunity, discountAmount, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined })\n )\n const lines = tx.getRepository(QuotationItem)\n if (old) await lines.softDelete({ quotation: { id: quotation.id } })\n await lines.save(items.map(({ receivedTaxAmount, ...item }) => lines.create({ ...item, quotation, domain, creator: user, updater: user })))\n\n const saved = await quotationInDomain(tx, domain, quotation.id)\n await recordQuotationEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED')\n return saved\n}\n\nexport async function transitionQuotation(tx: EntityManager, domain: Domain, user: any, id: string, action: QuotationAction, reason?: string) {\n await lockDocumentDomain(tx, domain)\n const quotation = await quotationInDomain(tx, domain, id)\n const next = quotationTransition(quotation.status, action, quotation.validUntil, calendarDay(domain.timezone))\n if (!next) throw new Refusal('quotation-transition-invalid', {}, 'The quotation status or validity does not allow this')\n if (next === QuotationStatus.REJECTED && !reason?.trim()) throw new Refusal('quotation-reason-required', {}, 'Say why the buyer declined')\n if (next === QuotationStatus.SENT) {\n if (!quotation.issueDate) throw new Refusal('quotation-issue-date-required', {}, 'A quotation is sent with its issue date')\n /* What the amounts were reckoned with stays on the quotation; a later rule never recomputes them. */\n quotation.taxRounding = TAX_ROUNDING\n quotation.currencyDigits = currencyDigits(quotation.currency)\n }\n quotation.status = next\n if (next === QuotationStatus.REJECTED) quotation.outcomeReason = reason!.trim()\n quotation.updater = user\n const { items, ...head } = quotation\n await tx.getRepository(Quotation).save(head)\n await recordQuotationEvent(tx, domain, user, quotation, action, reason)\n return quotationInDomain(tx, domain, id)\n}\n\n/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */\nexport async function reviseQuotation(tx: EntityManager, domain: Domain, user: any, id: string) {\n await lockDocumentDomain(tx, domain)\n const old = await quotationInDomain(tx, domain, id)\n if (!revisable(old.status)) throw new Refusal('quotation-revision-invalid', {}, 'Only a sent quotation nobody accepted can be revised')\n const versions = await tx.getRepository(Quotation).find({ where: { domain: { id: domain.id }, number: old.number } })\n if (old.version !== Math.max(...versions.map(one => one.version))) throw new Refusal('quotation-latest-required', {}, 'Revise the latest version')\n\n const repository = tx.getRepository(Quotation)\n const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, opportunityId, ...document } = old\n /* A revision answers the same opportunity as the version it replaces. */\n const opportunity = opportunityId ? ({ id: opportunityId } as Opportunity) : null\n const revision = await repository.save(repository.create({ ...document, opportunity, version: old.version + 1, status: QuotationStatus.DRAFT, domain, creator: user, updater: user }))\n const lines = tx.getRepository(QuotationItem)\n await lines.save(\n items.map(({ id: _line, quotation: _parent, quotationId, createdAt, updatedAt, deletedAt, creator, updater, ...line }) =>\n lines.create({ ...line, quotation: revision, domain, creator: user, updater: user })\n )\n )\n\n old.status = QuotationStatus.SUPERSEDED\n old.updater = user\n const { items: _items, ...head } = old\n await repository.save(head)\n await recordQuotationEvent(tx, domain, user, old, 'SUPERSEDED')\n const saved = await quotationInDomain(tx, domain, revision.id)\n await recordQuotationEvent(tx, domain, user, saved, 'REVISED')\n return saved\n}\n\nfunction recordQuotationEvent(tx: EntityManager, domain: Domain, user: any, quotation: Quotation, action: string, reason?: string) {\n return recordSalesEvent(tx, QuotationEvent, { quotation }, { domain, user, action, reason, snapshot: snapshotOf(quotation) })\n}\n\n/** The quotation as printed. */\nexport function snapshotOf(quotation: Quotation) {\n const keys = ['id', ...HEADER_KEYS, 'version', 'status', 'validUntil', 'opportunityId', 'discountAmount', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason']\n return {\n ...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),\n items: quotation.items.map(({ position, description, quantity, unit, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({\n position,\n description,\n quantity,\n unit: unit ?? null,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount\n }))\n }\n}\n\n/** The quotations that answer an opportunity, every version, by number then version. */\nexport function quotationsOfOpportunity(tx: EntityManager | undefined, domain: Domain, opportunityId: string) {\n const repository = tx ? tx.getRepository(Quotation) : getRepository(Quotation)\n return repository.find({ where: { domain: { id: domain.id }, opportunity: { id: opportunityId } }, order: { number: 'ASC', version: 'ASC' } })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"quotation-service.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { calculateDocument, currencyDigits, HEADER_KEYS, lockDocumentDomain, normalizedHeader, TAX_ROUNDING, validDay } from '@things-factory/invoice'\nimport { calendarDay, countListByField, Domain, getRepository, ListParam, Refusal } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { Opportunity } from '../opportunity/opportunity'\nimport { recordSalesEvent } from '../sales-event'\nimport { Quotation, QuotationStatus } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\nimport { QuotationAction, quotationTransition, revisable } from './quotation-rules'\nimport { NewQuotation } from './quotation-type'\n\nexport async function quotationInDomain(tx: EntityManager, domain: Domain, id: string) {\n const quotation = await tx.getRepository(Quotation).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] })\n if (!quotation) throw new Refusal('quotation-not-found', {}, 'Quotation not found')\n quotation.items.sort((a, b) => a.position - b.position)\n return quotation\n}\n\nexport async function saveQuotation(tx: EntityManager, domain: Domain, user: any, input: NewQuotation, id?: string) {\n await lockDocumentDomain(tx, domain)\n const old = id ? await quotationInDomain(tx, domain, id) : undefined\n if (old && old.status !== QuotationStatus.DRAFT) throw new Refusal('quotation-draft-required', {}, 'Revise a sent quotation to change it')\n\n const header = normalizedHeader(input)\n if (!validDay(input.validUntil)) throw new Refusal('quotation-valid-until', {}, 'A valid last day is required')\n /* A revision answers the quotation it revises: it keeps the number. */\n if (old && old.version > 1 && header.number !== old.number) throw new Refusal('quotation-revision-number', {}, 'A revision keeps its quotation number')\n\n const repository = tx.getRepository(Quotation)\n const version = old?.version ?? 1\n const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } })\n if (duplicate && duplicate.id !== id) throw new Refusal('quotation-number-duplicate', {}, 'Quotation number already exists')\n\n const opportunity = input.opportunityId ? await tx.getRepository(Opportunity).findOne({ where: { id: input.opportunityId, domain: { id: domain.id } } }) : null\n if (input.opportunityId && !opportunity) throw new Refusal('quotation-opportunity-invalid', {}, 'Choose an opportunity of this domain')\n\n const { items, discountAmount, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency, false, input.discountAmount)\n const quotation = await repository.save(\n repository.create({ ...old, ...header, version, validUntil: input.validUntil, opportunity, discountAmount, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined })\n )\n const lines = tx.getRepository(QuotationItem)\n if (old) await lines.softDelete({ quotation: { id: quotation.id } })\n await lines.save(items.map(({ receivedTaxAmount, ...item }) => lines.create({ ...item, quotation, domain, creator: user, updater: user })))\n\n const saved = await quotationInDomain(tx, domain, quotation.id)\n await recordQuotationEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED')\n return saved\n}\n\nexport async function transitionQuotation(tx: EntityManager, domain: Domain, user: any, id: string, action: QuotationAction, reason?: string) {\n await lockDocumentDomain(tx, domain)\n const quotation = await quotationInDomain(tx, domain, id)\n const next = quotationTransition(quotation.status, action, quotation.validUntil, calendarDay(domain.timezone))\n if (!next) throw new Refusal('quotation-transition-invalid', {}, 'The quotation status or validity does not allow this')\n if (next === QuotationStatus.REJECTED && !reason?.trim()) throw new Refusal('quotation-reason-required', {}, 'Say why the buyer declined')\n if (next === QuotationStatus.SENT) {\n if (!quotation.issueDate) throw new Refusal('quotation-issue-date-required', {}, 'A quotation is sent with its issue date')\n /* What the amounts were reckoned with stays on the quotation; a later rule never recomputes them. */\n quotation.taxRounding = TAX_ROUNDING\n quotation.currencyDigits = currencyDigits(quotation.currency)\n }\n quotation.status = next\n if (next === QuotationStatus.REJECTED) quotation.outcomeReason = reason!.trim()\n quotation.updater = user\n const { items, ...head } = quotation\n await tx.getRepository(Quotation).save(head)\n await recordQuotationEvent(tx, domain, user, quotation, action, reason)\n return quotationInDomain(tx, domain, id)\n}\n\n/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */\nexport async function reviseQuotation(tx: EntityManager, domain: Domain, user: any, id: string) {\n await lockDocumentDomain(tx, domain)\n const old = await quotationInDomain(tx, domain, id)\n if (!revisable(old.status)) throw new Refusal('quotation-revision-invalid', {}, 'Only a sent quotation nobody accepted can be revised')\n const versions = await tx.getRepository(Quotation).find({ where: { domain: { id: domain.id }, number: old.number } })\n if (old.version !== Math.max(...versions.map(one => one.version))) throw new Refusal('quotation-latest-required', {}, 'Revise the latest version')\n\n const repository = tx.getRepository(Quotation)\n const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, opportunityId, ...document } = old\n /* A revision answers the same opportunity as the version it replaces. */\n const opportunity = opportunityId ? ({ id: opportunityId } as Opportunity) : null\n const revision = await repository.save(repository.create({ ...document, opportunity, version: old.version + 1, status: QuotationStatus.DRAFT, domain, creator: user, updater: user }))\n const lines = tx.getRepository(QuotationItem)\n await lines.save(\n items.map(({ id: _line, quotation: _parent, quotationId, createdAt, updatedAt, deletedAt, creator, updater, ...line }) =>\n lines.create({ ...line, quotation: revision, domain, creator: user, updater: user })\n )\n )\n\n old.status = QuotationStatus.SUPERSEDED\n old.updater = user\n const { items: _items, ...head } = old\n await repository.save(head)\n await recordQuotationEvent(tx, domain, user, old, 'SUPERSEDED')\n const saved = await quotationInDomain(tx, domain, revision.id)\n await recordQuotationEvent(tx, domain, user, saved, 'REVISED')\n return saved\n}\n\nfunction recordQuotationEvent(tx: EntityManager, domain: Domain, user: any, quotation: Quotation, action: string, reason?: string) {\n return recordSalesEvent(tx, QuotationEvent, { quotation }, { domain, user, action, reason, snapshot: snapshotOf(quotation) })\n}\n\n/** The quotation as printed. */\nexport function snapshotOf(quotation: Quotation) {\n const keys = ['id', ...HEADER_KEYS, 'version', 'status', 'validUntil', 'opportunityId', 'discountAmount', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason']\n return {\n ...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),\n items: quotation.items.map(({ position, description, quantity, unit, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({\n position,\n description,\n quantity,\n unit: unit ?? null,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount\n }))\n }\n}\n\n/** The quotations that answer an opportunity, every version, by number then version. */\nexport function quotationsOfOpportunity(tx: EntityManager | undefined, domain: Domain, opportunityId: string) {\n const repository = tx ? tx.getRepository(Quotation) : getRepository(Quotation)\n return repository.find({ where: { domain: { id: domain.id }, opportunity: { id: opportunityId } }, order: { number: 'ASC', version: 'ASC' } })\n}\n\n/** What the quotation list finds quotations by, and its status counts alike. */\nexport const QUOTATION_SEARCHABLES = ['number', 'supplierName', 'buyerName', 'notes']\n\n/** How many quotations each status chip of the list would show, under the list's other conditions, in status order. */\nexport async function quotationStatusCounts(tx: EntityManager, domain: Domain, params: ListParam) {\n const counted = await countListByField({ domain, params, repository: tx.getRepository(Quotation), searchables: QUOTATION_SEARCHABLES, field: 'status' })\n return Object.values(QuotationStatus).map(value => ({ value, count: counted.get(value) ?? 0 }))\n}\n"]}
|
|
@@ -5,6 +5,10 @@ import { SalesProductList } from './sales-product-type';
|
|
|
5
5
|
export declare class SalesProductQuery {
|
|
6
6
|
salesProduct(id: string, context: ResolverContext): Promise<SalesProduct>;
|
|
7
7
|
salesProducts(params: ListParam, context: ResolverContext): Promise<SalesProductList>;
|
|
8
|
+
salesProductActiveCounts(params: ListParam, context: ResolverContext): Promise<{
|
|
9
|
+
value: string;
|
|
10
|
+
count: number;
|
|
11
|
+
}[]>;
|
|
8
12
|
domain(row: SalesProduct): Domain | Promise<Domain>;
|
|
9
13
|
creator(row: SalesProduct): User | Promise<User>;
|
|
10
14
|
updater(row: SalesProduct): User | Promise<User>;
|
|
@@ -6,6 +6,7 @@ const auth_base_1 = require("@things-factory/auth-base");
|
|
|
6
6
|
const shell_1 = require("@things-factory/shell");
|
|
7
7
|
const type_graphql_1 = require("type-graphql");
|
|
8
8
|
const sales_product_1 = require("./sales-product");
|
|
9
|
+
const sales_product_service_1 = require("./sales-product-service");
|
|
9
10
|
const sales_product_type_1 = require("./sales-product-type");
|
|
10
11
|
let SalesProductQuery = class SalesProductQuery {
|
|
11
12
|
salesProduct(id, context) {
|
|
@@ -16,11 +17,14 @@ let SalesProductQuery = class SalesProductQuery {
|
|
|
16
17
|
domain: context.state.domain,
|
|
17
18
|
params,
|
|
18
19
|
repository: (0, shell_1.getRepository)(sales_product_1.SalesProduct),
|
|
19
|
-
searchables:
|
|
20
|
+
searchables: sales_product_service_1.PRODUCT_SEARCHABLES
|
|
20
21
|
});
|
|
21
22
|
const [items, total] = await query.getManyAndCount();
|
|
22
23
|
return { items, total };
|
|
23
24
|
}
|
|
25
|
+
salesProductActiveCounts(params, context) {
|
|
26
|
+
return (0, sales_product_service_1.salesProductActiveCounts)((0, shell_1.getRepository)(sales_product_1.SalesProduct).manager, context.state.domain, params);
|
|
27
|
+
}
|
|
24
28
|
domain(row) {
|
|
25
29
|
return row.domain ?? (row.domainId ? (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: row.domainId }) : undefined);
|
|
26
30
|
}
|
|
@@ -50,6 +54,15 @@ tslib_1.__decorate([
|
|
|
50
54
|
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
51
55
|
tslib_1.__metadata("design:returntype", Promise)
|
|
52
56
|
], SalesProductQuery.prototype, "salesProducts", null);
|
|
57
|
+
tslib_1.__decorate([
|
|
58
|
+
(0, type_graphql_1.Directive)('@privilege(category: "sales", privilege: "query", domainOwnerGranted: true)'),
|
|
59
|
+
(0, type_graphql_1.Query)(type => [shell_1.ListValueCount], { description: 'To count the products in use (active) and out of use (inactive), under the list conditions other than active' }),
|
|
60
|
+
tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
|
|
61
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
62
|
+
tslib_1.__metadata("design:type", Function),
|
|
63
|
+
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
64
|
+
tslib_1.__metadata("design:returntype", void 0)
|
|
65
|
+
], SalesProductQuery.prototype, "salesProductActiveCounts", null);
|
|
53
66
|
tslib_1.__decorate([
|
|
54
67
|
(0, type_graphql_1.FieldResolver)(type => shell_1.Domain, { nullable: true }),
|
|
55
68
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|