@things-factory/sales 10.1.176 → 10.1.177

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist-client/opportunity-attach.js.map +1 -1
  2. package/dist-client/pages/opportunity-editor.d.ts +2 -0
  3. package/dist-client/pages/opportunity-editor.js +50 -4
  4. package/dist-client/pages/opportunity-editor.js.map +1 -1
  5. package/dist-client/pages/quotation-editor.d.ts +10 -0
  6. package/dist-client/pages/quotation-editor.js +50 -6
  7. package/dist-client/pages/quotation-editor.js.map +1 -1
  8. package/dist-client/tsconfig.tsbuildinfo +1 -1
  9. package/dist-server/service/index.d.ts +2 -2
  10. package/dist-server/service/opportunity/index.d.ts +1 -1
  11. package/dist-server/service/opportunity/opportunity-query.d.ts +2 -0
  12. package/dist-server/service/opportunity/opportunity-query.js +12 -0
  13. package/dist-server/service/opportunity/opportunity-query.js.map +1 -1
  14. package/dist-server/service/quotation/index.d.ts +2 -2
  15. package/dist-server/service/quotation/quotation-query.d.ts +2 -0
  16. package/dist-server/service/quotation/quotation-query.js +11 -0
  17. package/dist-server/service/quotation/quotation-query.js.map +1 -1
  18. package/dist-server/service/quotation/quotation-service.d.ts +2 -0
  19. package/dist-server/service/quotation/quotation-service.js +16 -4
  20. package/dist-server/service/quotation/quotation-service.js.map +1 -1
  21. package/dist-server/service/quotation/quotation-type.d.ts +1 -0
  22. package/dist-server/service/quotation/quotation-type.js +4 -0
  23. package/dist-server/service/quotation/quotation-type.js.map +1 -1
  24. package/dist-server/service/quotation/quotation.d.ts +3 -0
  25. package/dist-server/service/quotation/quotation.js +12 -0
  26. package/dist-server/service/quotation/quotation.js.map +1 -1
  27. package/dist-server/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +3 -3
  29. package/translations/en.json +5 -1
  30. package/translations/es.json +5 -1
  31. package/translations/ja.json +5 -1
  32. package/translations/ko.json +5 -1
  33. package/translations/ms.json +5 -1
  34. package/translations/zh.json +5 -1
@@ -9,8 +9,8 @@ export * from './opportunity/opportunity-event';
9
9
  export * from './opportunity/opportunity-rules';
10
10
  export * from './opportunity/opportunity-service';
11
11
  export * from './opportunity/opportunity-type';
12
- export declare const entities: (typeof import("./quotation/quotation-item").QuotationItem | typeof import("./quotation/quotation").Quotation | typeof import("./quotation/quotation-event").QuotationEvent | typeof import("./opportunity/opportunity").Opportunity | typeof import("./opportunity/opportunity-event").OpportunityEvent)[];
12
+ export declare const entities: (typeof import("./quotation/quotation").Quotation | typeof import("./quotation/quotation-item").QuotationItem | typeof import("./quotation/quotation-event").QuotationEvent | typeof import("./opportunity/opportunity").Opportunity | typeof import("./opportunity/opportunity-event").OpportunityEvent)[];
13
13
  export declare const subscribers: any[];
14
14
  export declare const schema: {
15
- resolverClasses: (typeof import("./opportunity/opportunity-event-query").OpportunityEventQuery | typeof import("./opportunity/opportunity-mutation").OpportunityMutation | typeof import("./opportunity/opportunity-query").OpportunityQuery | typeof import("./quotation/quotation-event-query").QuotationEventQuery | typeof import("./quotation/quotation-item-query").QuotationItemQuery | typeof import("./quotation/quotation-mutation").QuotationMutation | typeof import("./quotation/quotation-query").QuotationQuery)[];
15
+ resolverClasses: (typeof import("./quotation/quotation-query").QuotationQuery | typeof import("./quotation/quotation-mutation").QuotationMutation | typeof import("./quotation/quotation-item-query").QuotationItemQuery | typeof import("./quotation/quotation-event-query").QuotationEventQuery | typeof import("./opportunity/opportunity-query").OpportunityQuery | typeof import("./opportunity/opportunity-mutation").OpportunityMutation | typeof import("./opportunity/opportunity-event-query").OpportunityEventQuery)[];
16
16
  };
@@ -4,5 +4,5 @@ import { OpportunityEventQuery } from './opportunity-event-query';
4
4
  import { OpportunityMutation } from './opportunity-mutation';
5
5
  import { OpportunityQuery } from './opportunity-query';
6
6
  export declare const entities: (typeof Opportunity | typeof OpportunityEvent)[];
7
- export declare const resolvers: (typeof OpportunityEventQuery | typeof OpportunityMutation | typeof OpportunityQuery)[];
7
+ export declare const resolvers: (typeof OpportunityQuery | typeof OpportunityMutation | typeof OpportunityEventQuery)[];
8
8
  export declare const subscribers: any[];
@@ -1,6 +1,7 @@
1
1
  import { User } from '@things-factory/auth-base';
2
2
  import { Counterparty } from '@things-factory/biz-base';
3
3
  import { Domain, ListParam } from '@things-factory/shell';
4
+ import { Quotation } from '../quotation/quotation';
4
5
  import { Opportunity } from './opportunity';
5
6
  import { OpportunityEvent } from './opportunity-event';
6
7
  import { OpportunityList, OpportunityStageDefault } from './opportunity-type';
@@ -10,6 +11,7 @@ export declare class OpportunityQuery {
10
11
  opportunityEvents(opportunityId: string, context: ResolverContext): Promise<OpportunityEvent[]>;
11
12
  opportunityStages(): OpportunityStageDefault[];
12
13
  opportunityOwners(context: ResolverContext): Promise<User[]>;
14
+ quotations(row: Opportunity): Promise<Quotation[]>;
13
15
  counterparty(row: Opportunity): Counterparty | Promise<Counterparty>;
14
16
  endCustomer(row: Opportunity): Counterparty | Promise<Counterparty>;
15
17
  owner(row: Opportunity): User | Promise<User>;
@@ -6,6 +6,8 @@ const auth_base_1 = require("@things-factory/auth-base");
6
6
  const biz_base_1 = require("@things-factory/biz-base");
7
7
  const shell_1 = require("@things-factory/shell");
8
8
  const type_graphql_1 = require("type-graphql");
9
+ const quotation_1 = require("../quotation/quotation");
10
+ const quotation_service_1 = require("../quotation/quotation-service");
9
11
  const opportunity_1 = require("./opportunity");
10
12
  const opportunity_event_1 = require("./opportunity-event");
11
13
  const opportunity_rules_1 = require("./opportunity-rules");
@@ -33,6 +35,9 @@ let OpportunityQuery = class OpportunityQuery {
33
35
  opportunityOwners(context) {
34
36
  return (0, auth_base_1.getDomainAssignees)(context.state.domain);
35
37
  }
38
+ quotations(row) {
39
+ return (0, quotation_service_1.quotationsOfOpportunity)(undefined, { id: row.domainId }, row.id);
40
+ }
36
41
  counterparty(row) {
37
42
  return row.counterparty ?? (row.counterpartyId ? (0, shell_1.getRepository)(biz_base_1.Counterparty).findOneBy({ id: row.counterpartyId }) : undefined);
38
43
  }
@@ -95,6 +100,13 @@ tslib_1.__decorate([
95
100
  tslib_1.__metadata("design:paramtypes", [Object]),
96
101
  tslib_1.__metadata("design:returntype", void 0)
97
102
  ], OpportunityQuery.prototype, "opportunityOwners", null);
103
+ tslib_1.__decorate([
104
+ (0, type_graphql_1.FieldResolver)(type => [quotation_1.Quotation], { description: 'The quotations that answer the opportunity, every version' }),
105
+ tslib_1.__param(0, (0, type_graphql_1.Root)()),
106
+ tslib_1.__metadata("design:type", Function),
107
+ tslib_1.__metadata("design:paramtypes", [opportunity_1.Opportunity]),
108
+ tslib_1.__metadata("design:returntype", void 0)
109
+ ], OpportunityQuery.prototype, "quotations", null);
98
110
  tslib_1.__decorate([
99
111
  (0, type_graphql_1.FieldResolver)(type => biz_base_1.Counterparty, { nullable: true }),
100
112
  tslib_1.__param(0, (0, type_graphql_1.Root)()),
@@ -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 { 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 => 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 { 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"]}
@@ -5,6 +5,6 @@ import { QuotationItem } from './quotation-item';
5
5
  import { QuotationItemQuery } from './quotation-item-query';
6
6
  import { QuotationMutation } from './quotation-mutation';
7
7
  import { QuotationQuery } from './quotation-query';
8
- export declare const entities: (typeof QuotationItem | typeof Quotation | typeof QuotationEvent)[];
9
- export declare const resolvers: (typeof QuotationEventQuery | typeof QuotationItemQuery | typeof QuotationMutation | typeof QuotationQuery)[];
8
+ export declare const entities: (typeof Quotation | typeof QuotationItem | typeof QuotationEvent)[];
9
+ export declare const resolvers: (typeof QuotationQuery | typeof QuotationMutation | typeof QuotationItemQuery | typeof QuotationEventQuery)[];
10
10
  export declare const subscribers: any[];
@@ -1,5 +1,6 @@
1
1
  import { User } from '@things-factory/auth-base';
2
2
  import { Domain, ListParam } from '@things-factory/shell';
3
+ import { Opportunity } from '../opportunity/opportunity';
3
4
  import { Quotation } from './quotation';
4
5
  import { QuotationEvent } from './quotation-event';
5
6
  import { QuotationItem } from './quotation-item';
@@ -11,6 +12,7 @@ export declare class QuotationQuery {
11
12
  quotationEvents(quotationId: string, context: ResolverContext): Promise<QuotationEvent[]>;
12
13
  items(quotation: Quotation): Promise<QuotationItem[]>;
13
14
  taxGroups(quotation: Quotation): Promise<import("@things-factory/invoice").TaxGroup[]>;
15
+ opportunity(row: Quotation): Opportunity | Promise<Opportunity>;
14
16
  domain(row: Quotation): Domain | Promise<Domain>;
15
17
  creator(row: Quotation): User | Promise<User>;
16
18
  updater(row: Quotation): User | Promise<User>;
@@ -6,6 +6,7 @@ const auth_base_1 = require("@things-factory/auth-base");
6
6
  const invoice_1 = require("@things-factory/invoice");
7
7
  const shell_1 = require("@things-factory/shell");
8
8
  const type_graphql_1 = require("type-graphql");
9
+ const opportunity_1 = require("../opportunity/opportunity");
9
10
  const quotation_1 = require("./quotation");
10
11
  const quotation_event_1 = require("./quotation-event");
11
12
  const quotation_item_1 = require("./quotation-item");
@@ -36,6 +37,9 @@ let QuotationQuery = class QuotationQuery {
36
37
  async taxGroups(quotation) {
37
38
  return (0, invoice_1.taxGroupsOf)({ ...quotation, items: await this.items(quotation) });
38
39
  }
40
+ opportunity(row) {
41
+ return row.opportunity ?? (row.opportunityId ? (0, shell_1.getRepository)(opportunity_1.Opportunity).findOneBy({ id: row.opportunityId }) : undefined);
42
+ }
39
43
  domain(row) {
40
44
  return row.domain ?? (row.domainId ? (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: row.domainId }) : undefined);
41
45
  }
@@ -97,6 +101,13 @@ tslib_1.__decorate([
97
101
  tslib_1.__metadata("design:paramtypes", [quotation_1.Quotation]),
98
102
  tslib_1.__metadata("design:returntype", Promise)
99
103
  ], QuotationQuery.prototype, "taxGroups", null);
104
+ tslib_1.__decorate([
105
+ (0, type_graphql_1.FieldResolver)(type => opportunity_1.Opportunity, { nullable: true }),
106
+ tslib_1.__param(0, (0, type_graphql_1.Root)()),
107
+ tslib_1.__metadata("design:type", Function),
108
+ tslib_1.__metadata("design:paramtypes", [quotation_1.Quotation]),
109
+ tslib_1.__metadata("design:returntype", void 0)
110
+ ], QuotationQuery.prototype, "opportunity", null);
100
111
  tslib_1.__decorate([
101
112
  (0, type_graphql_1.FieldResolver)(type => shell_1.Domain, { nullable: true }),
102
113
  tslib_1.__param(0, (0, type_graphql_1.Root)()),
@@ -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 { DocumentTaxGroup, taxGroupsOf } from '@things-factory/invoice'\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'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\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 @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 => 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 { DocumentTaxGroup, taxGroupsOf } from '@things-factory/invoice'\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 { Opportunity } from '../opportunity/opportunity'\nimport { Quotation } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\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 @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 => 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"]}
@@ -21,3 +21,5 @@ export declare function snapshotOf(quotation: Quotation): {
21
21
  supplyAmount: number;
22
22
  }[];
23
23
  };
24
+ /** The quotations that answer an opportunity, every version, by number then version. */
25
+ export declare function quotationsOfOpportunity(tx: EntityManager | undefined, domain: Domain, opportunityId: string): Promise<Quotation[]>;
@@ -5,8 +5,10 @@ exports.saveQuotation = saveQuotation;
5
5
  exports.transitionQuotation = transitionQuotation;
6
6
  exports.reviseQuotation = reviseQuotation;
7
7
  exports.snapshotOf = snapshotOf;
8
+ exports.quotationsOfOpportunity = quotationsOfOpportunity;
8
9
  const invoice_1 = require("@things-factory/invoice");
9
10
  const shell_1 = require("@things-factory/shell");
11
+ const opportunity_1 = require("../opportunity/opportunity");
10
12
  const sales_event_1 = require("../sales-event");
11
13
  const quotation_1 = require("./quotation");
12
14
  const quotation_event_1 = require("./quotation-event");
@@ -35,8 +37,11 @@ async function saveQuotation(tx, domain, user, input, id) {
35
37
  const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } });
36
38
  if (duplicate && duplicate.id !== id)
37
39
  throw new shell_1.Refusal('quotation-number-duplicate', {}, 'Quotation number already exists');
40
+ const opportunity = input.opportunityId ? await tx.getRepository(opportunity_1.Opportunity).findOne({ where: { id: input.opportunityId, domain: { id: domain.id } } }) : null;
41
+ if (input.opportunityId && !opportunity)
42
+ throw new shell_1.Refusal('quotation-opportunity-invalid', {}, 'Choose an opportunity of this domain');
38
43
  const { items, supplyAmount, taxAmount, totalAmount } = (0, invoice_1.calculateDocument)(input.items, header.currency);
39
- const quotation = await repository.save(repository.create({ ...old, ...header, version, validUntil: input.validUntil, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined }));
44
+ const quotation = await repository.save(repository.create({ ...old, ...header, version, validUntil: input.validUntil, opportunity, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined }));
40
45
  const lines = tx.getRepository(quotation_item_1.QuotationItem);
41
46
  if (old)
42
47
  await lines.softDelete({ quotation: { id: quotation.id } });
@@ -79,8 +84,10 @@ async function reviseQuotation(tx, domain, user, id) {
79
84
  if (old.version !== Math.max(...versions.map(one => one.version)))
80
85
  throw new shell_1.Refusal('quotation-latest-required', {}, 'Revise the latest version');
81
86
  const repository = tx.getRepository(quotation_1.Quotation);
82
- const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, ...document } = old;
83
- const revision = await repository.save(repository.create({ ...document, version: old.version + 1, status: quotation_1.QuotationStatus.DRAFT, domain, creator: user, updater: user }));
87
+ const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, opportunityId, ...document } = old;
88
+ /* A revision answers the same opportunity as the version it replaces. */
89
+ const opportunity = opportunityId ? { id: opportunityId } : null;
90
+ const revision = await repository.save(repository.create({ ...document, opportunity, version: old.version + 1, status: quotation_1.QuotationStatus.DRAFT, domain, creator: user, updater: user }));
84
91
  const lines = tx.getRepository(quotation_item_1.QuotationItem);
85
92
  await lines.save(items.map(({ id: _line, quotation: _parent, quotationId, createdAt, updatedAt, deletedAt, creator, updater, ...line }) => lines.create({ ...line, quotation: revision, domain, creator: user, updater: user })));
86
93
  old.status = quotation_1.QuotationStatus.SUPERSEDED;
@@ -97,7 +104,7 @@ function recordQuotationEvent(tx, domain, user, quotation, action, reason) {
97
104
  }
98
105
  /** The quotation as printed. */
99
106
  function snapshotOf(quotation) {
100
- const keys = ['id', ...invoice_1.HEADER_KEYS, 'version', 'status', 'validUntil', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason'];
107
+ const keys = ['id', ...invoice_1.HEADER_KEYS, 'version', 'status', 'validUntil', 'opportunityId', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason'];
101
108
  return {
102
109
  ...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),
103
110
  items: quotation.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({
@@ -112,4 +119,9 @@ function snapshotOf(quotation) {
112
119
  }))
113
120
  };
114
121
  }
122
+ /** The quotations that answer an opportunity, every version, by number then version. */
123
+ function quotationsOfOpportunity(tx, domain, opportunityId) {
124
+ const repository = tx ? tx.getRepository(quotation_1.Quotation) : (0, shell_1.getRepository)(quotation_1.Quotation);
125
+ return repository.find({ where: { domain: { id: domain.id }, opportunity: { id: opportunityId } }, order: { number: 'ASC', version: 'ASC' } });
126
+ }
115
127
  //# sourceMappingURL=quotation-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"quotation-service.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-service.ts"],"names":[],"mappings":";;AAWA,8CAKC;AAED,sCA0BC;AAED,kDAmBC;AAGD,0CAyBC;AAOD,gCAeC;AAnHD,qDAAmK;AACnK,iDAAuD;AAGvD,gDAAiD;AACjD,2CAAwD;AACxD,uDAAkD;AAClD,qDAAgD;AAChD,uDAAmF;AAG5E,KAAK,UAAU,iBAAiB,CAAC,EAAiB,EAAE,MAAc,EAAE,EAAU;IACnF,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,qBAAS,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IAC1I,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,eAAO,CAAC,qBAAqB,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAA;IACnF,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;IACvD,OAAO,SAAS,CAAA;AAClB,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,KAAmB,EAAE,EAAW;IAChH,MAAM,IAAA,4BAAkB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACpC,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACpE,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,2BAAe,CAAC,KAAK;QAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,sCAAsC,CAAC,CAAA;IAE1I,MAAM,MAAM,GAAG,IAAA,0BAAgB,EAAC,KAAK,CAAC,CAAA;IACtC,IAAI,CAAC,IAAA,kBAAQ,EAAC,KAAK,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,uBAAuB,EAAE,EAAE,EAAE,8BAA8B,CAAC,CAAA;IAC/G,uEAAuE;IACvE,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,uCAAuC,CAAC,CAAA;IAEvJ,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,qBAAS,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,OAAO,IAAI,CAAC,CAAA;IACjC,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;IACpH,IAAI,SAAS,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE;QAAE,MAAM,IAAI,eAAO,CAAC,4BAA4B,EAAE,EAAE,EAAE,iCAAiC,CAAC,CAAA;IAE5H,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,IAAA,2BAAiB,EAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;IACvG,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,IAAI,CACrC,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAC9L,CAAA;IACD,MAAM,KAAK,GAAG,EAAE,CAAC,aAAa,CAAC,8BAAa,CAAC,CAAA;IAC7C,IAAI,GAAG;QAAE,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IACpE,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAE3I,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IAC/D,MAAM,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAChF,OAAO,KAAK,CAAA;AACd,CAAC;AAEM,KAAK,UAAU,mBAAmB,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,EAAU,EAAE,MAAuB,EAAE,MAAe;IAC1I,MAAM,IAAA,4BAAkB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACpC,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;IACzD,MAAM,IAAI,GAAG,IAAA,qCAAmB,EAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,IAAA,qBAAW,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC9G,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,eAAO,CAAC,8BAA8B,EAAE,EAAE,EAAE,sDAAsD,CAAC,CAAA;IACxH,IAAI,IAAI,KAAK,2BAAe,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,4BAA4B,CAAC,CAAA;IAC1I,IAAI,IAAI,KAAK,2BAAe,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,SAAS,CAAC,SAAS;YAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,yCAAyC,CAAC,CAAA;QAC3H,qGAAqG;QACrG,SAAS,CAAC,WAAW,GAAG,sBAAY,CAAA;QACpC,SAAS,CAAC,cAAc,GAAG,IAAA,wBAAc,EAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC/D,CAAC;IACD,SAAS,CAAC,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,IAAI,KAAK,2BAAe,CAAC,QAAQ;QAAE,SAAS,CAAC,aAAa,GAAG,MAAO,CAAC,IAAI,EAAE,CAAA;IAC/E,SAAS,CAAC,OAAO,GAAG,IAAI,CAAA;IACxB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,SAAS,CAAA;IACpC,MAAM,EAAE,CAAC,aAAa,CAAC,qBAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC5C,MAAM,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACvE,OAAO,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,4GAA4G;AACrG,KAAK,UAAU,eAAe,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,EAAU;IAC5F,MAAM,IAAA,4BAAkB,EAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACpC,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;IACnD,IAAI,CAAC,IAAA,2BAAS,EAAC,GAAG,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,4BAA4B,EAAE,EAAE,EAAE,sDAAsD,CAAC,CAAA;IACvI,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,qBAAS,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrH,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,2BAA2B,CAAC,CAAA;IAElJ,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,qBAAS,CAAC,CAAA;IAC9C,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAA;IAC/I,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,MAAM,EAAE,2BAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACzK,MAAM,KAAK,GAAG,EAAE,CAAC,aAAa,CAAC,8BAAa,CAAC,CAAA;IAC7C,MAAM,KAAK,CAAC,IAAI,CACd,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CACvH,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CACrF,CACF,CAAA;IAED,GAAG,CAAC,MAAM,GAAG,2BAAe,CAAC,UAAU,CAAA;IACvC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAA;IAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,CAAA;IACtC,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,MAAM,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,YAAY,CAAC,CAAA;IAC/D,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC9D,MAAM,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;IAC9D,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,oBAAoB,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,SAAoB,EAAE,MAAc,EAAE,MAAe;IAC/H,OAAO,IAAA,8BAAgB,EAAC,EAAE,EAAE,gCAAc,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;AAC/H,CAAC;AAED,gCAAgC;AAChC,SAAgB,UAAU,CAAC,SAAoB;IAC7C,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,qBAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAA;IACpK,OAAO;QACL,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QACrE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;YACxH,QAAQ;YACR,WAAW;YACX,QAAQ;YACR,SAAS;YACT,QAAQ;YACR,OAAO;YACP,OAAO;YACP,YAAY;SACb,CAAC,CAAC;KACJ,CAAA;AACH,CAAC","sourcesContent":["import { calculateDocument, calendarDay, currencyDigits, HEADER_KEYS, lockDocumentDomain, normalizedHeader, TAX_ROUNDING, validDay } from '@things-factory/invoice'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\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 { items, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency)\n const quotation = await repository.save(\n repository.create({ ...old, ...header, version, validUntil: input.validUntil, 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, ...document } = old\n const revision = await repository.save(repository.create({ ...document, 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', '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, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({\n position,\n description,\n quantity,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount\n }))\n }\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, calendarDay, currencyDigits, HEADER_KEYS, lockDocumentDomain, normalizedHeader, TAX_ROUNDING, validDay } from '@things-factory/invoice'\nimport { Domain, getRepository, 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, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency)\n const quotation = await repository.save(\n repository.create({ ...old, ...header, version, validUntil: input.validUntil, opportunity, 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', '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, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({\n position,\n description,\n quantity,\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"]}
@@ -2,6 +2,7 @@ import { DocumentInput } from '@things-factory/invoice';
2
2
  import { Quotation } from './quotation';
3
3
  export declare class NewQuotation extends DocumentInput {
4
4
  validUntil: string;
5
+ opportunityId?: string;
5
6
  }
6
7
  export declare class QuotationList {
7
8
  items: Quotation[];
@@ -12,6 +12,10 @@ tslib_1.__decorate([
12
12
  (0, type_graphql_1.Field)({ description: 'Last day the offer holds, YYYY-MM-DD' }),
13
13
  tslib_1.__metadata("design:type", String)
14
14
  ], NewQuotation.prototype, "validUntil", void 0);
15
+ tslib_1.__decorate([
16
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Id of the sales opportunity the quotation answers; none when empty' }),
17
+ tslib_1.__metadata("design:type", String)
18
+ ], NewQuotation.prototype, "opportunityId", void 0);
15
19
  exports.NewQuotation = NewQuotation = tslib_1.__decorate([
16
20
  (0, type_graphql_1.InputType)({ description: 'A quotation draft to create or replace' })
17
21
  ], NewQuotation);
@@ -1 +1 @@
1
- {"version":3,"file":"quotation-type.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-type.ts"],"names":[],"mappings":";;;;AAAA,qDAAuD;AACvD,+CAAgE;AAEhE,2CAAuC;AAGhC,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,uBAAa;CAE9C,CAAA;AAFY,oCAAY;AACyC;IAA/D,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;gDAAmB;uBADvE,YAAY;IADxB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;GACxD,YAAY,CAExB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAGzB,CAAA;AAHY,sCAAa;AAC+C;IAAtE,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,qBAAS,CAAC,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;4CAAmB;AACC;IAAzF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;4CAAc;wBAF5F,aAAa;IADzB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;GACvC,aAAa,CAGzB","sourcesContent":["import { DocumentInput } from '@things-factory/invoice'\nimport { Field, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Quotation } from './quotation'\n\n@InputType({ description: 'A quotation draft to create or replace' })\nexport class NewQuotation extends DocumentInput {\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' }) validUntil: string\n}\n\n@ObjectType({ description: 'A page of quotations' })\nexport class QuotationList {\n @Field(type => [Quotation], { description: 'Quotations on the page' }) items: Quotation[]\n @Field(type => Int, { description: 'Number of quotations matching the list parameters' }) total: number\n}\n"]}
1
+ {"version":3,"file":"quotation-type.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-type.ts"],"names":[],"mappings":";;;;AAAA,qDAAuD;AACvD,+CAAgE;AAEhE,2CAAuC;AAGhC,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,uBAAa;CAG9C,CAAA;AAHY,oCAAY;AACyC;IAA/D,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;gDAAmB;AAC4B;IAA7G,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oEAAoE,EAAE,CAAC;;mDAAuB;uBAFzH,YAAY;IADxB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;GACxD,YAAY,CAGxB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAGzB,CAAA;AAHY,sCAAa;AAC+C;IAAtE,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,qBAAS,CAAC,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;4CAAmB;AACC;IAAzF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;4CAAc;wBAF5F,aAAa;IADzB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;GACvC,aAAa,CAGzB","sourcesContent":["import { DocumentInput } from '@things-factory/invoice'\nimport { Field, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Quotation } from './quotation'\n\n@InputType({ description: 'A quotation draft to create or replace' })\nexport class NewQuotation extends DocumentInput {\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' }) validUntil: string\n @Field({ nullable: true, description: 'Id of the sales opportunity the quotation answers; none when empty' }) opportunityId?: string\n}\n\n@ObjectType({ description: 'A page of quotations' })\nexport class QuotationList {\n @Field(type => [Quotation], { description: 'Quotations on the page' }) items: Quotation[]\n @Field(type => Int, { description: 'Number of quotations matching the list parameters' }) total: number\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { User } from '@things-factory/auth-base';
2
2
  import { CommercialDocument } from '@things-factory/invoice';
3
3
  import { Domain } from '@things-factory/shell';
4
+ import { Opportunity } from '../opportunity/opportunity';
4
5
  import { QuotationItem } from './quotation-item';
5
6
  export declare enum QuotationStatus {
6
7
  DRAFT = "DRAFT",
@@ -22,6 +23,8 @@ export declare class Quotation extends CommercialDocument {
22
23
  status: QuotationStatus;
23
24
  validUntil: string;
24
25
  outcomeReason?: string;
26
+ opportunity?: Opportunity;
27
+ opportunityId?: string;
25
28
  items: QuotationItem[];
26
29
  createdAt?: Date;
27
30
  updatedAt?: Date;
@@ -7,6 +7,7 @@ const invoice_1 = require("@things-factory/invoice");
7
7
  const shell_1 = require("@things-factory/shell");
8
8
  const type_graphql_1 = require("type-graphql");
9
9
  const typeorm_1 = require("typeorm");
10
+ const opportunity_1 = require("../opportunity/opportunity");
10
11
  const quotation_item_1 = require("./quotation-item");
11
12
  var QuotationStatus;
12
13
  (function (QuotationStatus) {
@@ -62,6 +63,16 @@ tslib_1.__decorate([
62
63
  (0, type_graphql_1.Field)({ nullable: true, description: 'Why the buyer declined, when rejected' }),
63
64
  tslib_1.__metadata("design:type", String)
64
65
  ], Quotation.prototype, "outcomeReason", void 0);
66
+ tslib_1.__decorate([
67
+ (0, typeorm_1.ManyToOne)(type => opportunity_1.Opportunity, { nullable: true }),
68
+ (0, type_graphql_1.Field)(type => opportunity_1.Opportunity, { nullable: true, description: 'The sales opportunity the quotation answers, when there is one' }),
69
+ tslib_1.__metadata("design:type", opportunity_1.Opportunity)
70
+ ], Quotation.prototype, "opportunity", void 0);
71
+ tslib_1.__decorate([
72
+ (0, typeorm_1.RelationId)((quotation) => quotation.opportunity),
73
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Id of the sales opportunity the quotation answers' }),
74
+ tslib_1.__metadata("design:type", String)
75
+ ], Quotation.prototype, "opportunityId", void 0);
65
76
  tslib_1.__decorate([
66
77
  (0, typeorm_1.OneToMany)(type => quotation_item_1.QuotationItem, item => item.quotation),
67
78
  (0, type_graphql_1.Field)(type => [quotation_item_1.QuotationItem], { description: 'Lines in printed order' }),
@@ -104,6 +115,7 @@ exports.Quotation = Quotation = tslib_1.__decorate([
104
115
  (0, typeorm_1.Entity)(),
105
116
  (0, typeorm_1.Index)('ix_quotation_0', (quotation) => [quotation.domain, quotation.number, quotation.version], { unique: true, where: '"deleted_at" IS NULL' }),
106
117
  (0, typeorm_1.Index)('ix_quotation_1', (quotation) => [quotation.domain, quotation.status, quotation.validUntil]),
118
+ (0, typeorm_1.Index)('ix_quotation_2', (quotation) => [quotation.domain, quotation.opportunity]),
107
119
  (0, type_graphql_1.ObjectType)({ description: 'Entity for Quotation — a priced offer, kept with every revision' })
108
120
  ], Quotation);
109
121
  //# sourceMappingURL=quotation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"quotation.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { CommercialDocument } from '@things-factory/invoice'\nimport { Domain } from '@things-factory/shell'\nimport { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\n\nimport { QuotationItem } from './quotation-item'\n\nexport enum QuotationStatus {\n DRAFT = 'DRAFT',\n SENT = 'SENT',\n ACCEPTED = 'ACCEPTED',\n REJECTED = 'REJECTED',\n EXPIRED = 'EXPIRED',\n SUPERSEDED = 'SUPERSEDED'\n}\nregisterEnumType(QuotationStatus, {\n name: 'QuotationStatus',\n description: 'state of a quotation — draft, sent, accepted, rejected, expired, or replaced by a revision'\n})\n\n/**\n * A quotation — the same printed head and lines as an invoice, valid until a day. A sent quotation is changed by\n * revising it: the revision keeps the number and takes the next version, and the one before is superseded.\n */\n@Entity()\n@Index('ix_quotation_0', (quotation: Quotation) => [quotation.domain, quotation.number, quotation.version], { unique: true, where: '\"deleted_at\" IS NULL' })\n@Index('ix_quotation_1', (quotation: Quotation) => [quotation.domain, quotation.status, quotation.validUntil])\n@ObjectType({ description: 'Entity for Quotation — a priced offer, kept with every revision' })\nexport class Quotation extends CommercialDocument {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true })\n domain?: Domain\n\n @RelationId((quotation: Quotation) => quotation.domain)\n domainId?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'Revision of the quotation number, from 1' })\n version: number\n\n @Column({ default: QuotationStatus.DRAFT })\n @Field(type => QuotationStatus, { description: 'Draft, sent, accepted, rejected, expired or superseded' })\n status: QuotationStatus\n\n @Column()\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' })\n validUntil: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why the buyer declined, when rejected' })\n outcomeReason?: string\n\n @OneToMany(type => QuotationItem, item => item.quotation)\n @Field(type => [QuotationItem], { description: 'Lines in printed order' })\n items: QuotationItem[]\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the quotation was created' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the quotation last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the quotation was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((quotation: Quotation) => quotation.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((quotation: Quotation) => quotation.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"quotation.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { CommercialDocument } from '@things-factory/invoice'\nimport { Domain } from '@things-factory/shell'\nimport { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\n\nimport { Opportunity } from '../opportunity/opportunity'\nimport { QuotationItem } from './quotation-item'\n\nexport enum QuotationStatus {\n DRAFT = 'DRAFT',\n SENT = 'SENT',\n ACCEPTED = 'ACCEPTED',\n REJECTED = 'REJECTED',\n EXPIRED = 'EXPIRED',\n SUPERSEDED = 'SUPERSEDED'\n}\nregisterEnumType(QuotationStatus, {\n name: 'QuotationStatus',\n description: 'state of a quotation — draft, sent, accepted, rejected, expired, or replaced by a revision'\n})\n\n/**\n * A quotation — the same printed head and lines as an invoice, valid until a day. A sent quotation is changed by\n * revising it: the revision keeps the number and takes the next version, and the one before is superseded.\n */\n@Entity()\n@Index('ix_quotation_0', (quotation: Quotation) => [quotation.domain, quotation.number, quotation.version], { unique: true, where: '\"deleted_at\" IS NULL' })\n@Index('ix_quotation_1', (quotation: Quotation) => [quotation.domain, quotation.status, quotation.validUntil])\n@Index('ix_quotation_2', (quotation: Quotation) => [quotation.domain, quotation.opportunity])\n@ObjectType({ description: 'Entity for Quotation — a priced offer, kept with every revision' })\nexport class Quotation extends CommercialDocument {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true })\n domain?: Domain\n\n @RelationId((quotation: Quotation) => quotation.domain)\n domainId?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'Revision of the quotation number, from 1' })\n version: number\n\n @Column({ default: QuotationStatus.DRAFT })\n @Field(type => QuotationStatus, { description: 'Draft, sent, accepted, rejected, expired or superseded' })\n status: QuotationStatus\n\n @Column()\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' })\n validUntil: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why the buyer declined, when rejected' })\n outcomeReason?: string\n\n /* An opportunity may have several quotations; a quotation answers at most one (ADR-0138 decision 7). */\n @ManyToOne(type => Opportunity, { nullable: true })\n @Field(type => Opportunity, { nullable: true, description: 'The sales opportunity the quotation answers, when there is one' })\n opportunity?: Opportunity\n\n @RelationId((quotation: Quotation) => quotation.opportunity)\n @Field({ nullable: true, description: 'Id of the sales opportunity the quotation answers' })\n opportunityId?: string\n\n @OneToMany(type => QuotationItem, item => item.quotation)\n @Field(type => [QuotationItem], { description: 'Lines in printed order' })\n items: QuotationItem[]\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the quotation was created' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the quotation last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the quotation was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((quotation: Quotation) => quotation.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((quotation: Quotation) => quotation.updater)\n updaterId?: string\n}\n"]}