1
1
import { mapOf } from '@seedcompany/common' ;
2
+ import { oneLine } from 'common-tags' ;
2
3
import { EventsHandler , ILogger , Logger } from '~/core' ;
3
4
import { ReportType } from '../../periodic-report/dto' ;
4
5
import { PeriodicReportUploadedEvent } from '../../periodic-report/events' ;
5
6
import { ProductService } from '../../product' ;
6
- import { ProducibleType } from '../../product/dto' ;
7
+ import { ProducibleType , ProductStep } from '../../product/dto' ;
7
8
import { isScriptureEqual } from '../../scripture' ;
8
9
import { ProgressReportVariantProgress as Progress } from '../dto' ;
9
10
import { ProductProgressService } from '../product-progress.service' ;
11
+ import { StepNotPlannedException } from '../step-not-planned.exception' ;
10
12
import { StepProgressExtractor } from '../step-progress-extractor.service' ;
11
13
12
14
@EventsHandler ( PeriodicReportUploadedEvent )
@@ -62,7 +64,7 @@ export class ExtractPnpProgressHandler {
62
64
if ( row . story ) {
63
65
const productId = storyProducts . get ( row . story ) ;
64
66
if ( productId ) {
65
- return { productId, steps } ;
67
+ return { extracted : row , productId, steps } ;
66
68
}
67
69
}
68
70
@@ -73,15 +75,19 @@ export class ExtractPnpProgressHandler {
73
75
isScriptureEqual ( ref . scriptureRanges , row . scripture ) ,
74
76
) ;
75
77
if ( exactScriptureMatch ) {
76
- return { productId : exactScriptureMatch . id , steps } ;
78
+ return { extracted : row , productId : exactScriptureMatch . id , steps } ;
77
79
}
78
80
79
81
const unspecifiedScriptureMatch = scriptureProducts . find (
80
82
( ref ) =>
81
83
ref . book === row . bookName && ref . totalVerses === row . totalVerses ,
82
84
) ;
83
85
if ( unspecifiedScriptureMatch ) {
84
- return { productId : unspecifiedScriptureMatch . id , steps } ;
86
+ return {
87
+ extracted : row ,
88
+ productId : unspecifiedScriptureMatch . id ,
89
+ steps,
90
+ } ;
85
91
}
86
92
}
87
93
@@ -95,16 +101,48 @@ export class ExtractPnpProgressHandler {
95
101
96
102
// Update progress for report & product
97
103
await Promise . all (
98
- updates . map ( async ( input ) => {
99
- await this . progress . update (
100
- {
101
- ...input ,
102
- reportId : event . report . id ,
103
- // TODO this seems fine for now as only this variant will upload PnPs.
104
- variant : Progress . FallbackVariant ,
105
- } ,
106
- event . session ,
107
- ) ;
104
+ updates . map ( async ( { extracted, ...input } ) => {
105
+ try {
106
+ await this . progress . update (
107
+ {
108
+ ...input ,
109
+ reportId : event . report . id ,
110
+ // TODO this seems fine for now as only this variant will upload PnPs.
111
+ variant : Progress . FallbackVariant ,
112
+ } ,
113
+ event . session ,
114
+ ) ;
115
+ } catch ( e ) {
116
+ if (
117
+ ! (
118
+ e instanceof AggregateError &&
119
+ e . message === 'Invalid Progress Input'
120
+ )
121
+ ) {
122
+ throw e ;
123
+ }
124
+ for ( const error of e . errors ) {
125
+ if ( ! ( error instanceof StepNotPlannedException ) ) {
126
+ continue ;
127
+ }
128
+ const stepLabel = ProductStep . entry ( error . step ) . label ;
129
+ // kinda. close enough, I think, we give the cell ref as well.
130
+ const goalLabel = extracted . bookName ?? extracted . story ;
131
+ result . addProblem ( {
132
+ severity : 'Error' ,
133
+ groups : [
134
+ 'Step is not planned' ,
135
+ `_${ goalLabel } _ has progress reported on steps that have not been declared to be worked in this engagement` ,
136
+ `_${ goalLabel } _ has not declared _${ stepLabel } _ \`${ extracted . cell . ref } \` as a step that will be worked in this engagement` ,
137
+ ] ,
138
+ message : oneLine `
139
+ Please update the goal in CORD to mark this step as planned
140
+ or upload an updated PnP file to the "Planning Spreadsheet" on the engagement page.
141
+ ` ,
142
+ source : extracted . cell ,
143
+ } ) ;
144
+ }
145
+ }
108
146
} ) ,
109
147
) ;
110
148
}
0 commit comments