@@ -4,12 +4,13 @@ import * as actions from "store/actions/workPeriods";
4
4
import * as selectors from "store/selectors/workPeriods" ;
5
5
import * as services from "services/workPeriods" ;
6
6
import {
7
- SORT_BY_MAP ,
7
+ API_CHALLENGE_PAYMENT_STATUS ,
8
+ API_FIELDS_QUERY ,
8
9
API_SORT_BY ,
9
10
DATE_FORMAT_API ,
10
11
PAYMENT_STATUS_MAP ,
11
- API_FIELDS_QUERY ,
12
- API_CHALLENGE_PAYMENT_STATUS ,
12
+ SERVER_DATA_UPDATE_DELAY ,
13
+ SORT_BY_MAP ,
13
14
} from "constants/workPeriods" ;
14
15
import {
15
16
delay ,
@@ -21,6 +22,7 @@ import {
21
22
makeUrlQuery ,
22
23
normalizeBillingAccounts ,
23
24
normalizeDetailsPeriodItems ,
25
+ normalizePaymentData ,
24
26
normalizePeriodData ,
25
27
normalizePeriodItems ,
26
28
} from "utils/workPeriods" ;
@@ -34,37 +36,38 @@ import {
34
36
import { RESOURCE_BOOKING_STATUS , WORK_PERIODS_PATH } from "constants/index.js" ;
35
37
import { currencyFormatter } from "utils/formatters" ;
36
38
37
- export const loadWorkPeriodData = ( periodId ) => async ( dispatch , getState ) => {
38
- let [ periodsData ] = selectors . getWorkPeriodsData ( getState ( ) ) ;
39
- periodsData [ periodId ] ?. cancelSource ?. cancel ( ) ;
40
- const [ promise , source ] = services . fetchWorkPeriod ( periodId ) ;
41
- dispatch ( actions . setWorkPeriodDataPending ( periodId , source ) ) ;
42
- let userHandle = null ;
43
- let periodData = null ;
44
- let errorMessage = null ;
45
- try {
46
- const data = await promise ;
47
- userHandle = data . userHandle ;
48
- periodData = normalizePeriodData ( data ) ;
49
- } catch ( error ) {
50
- if ( ! axios . isCancel ( error ) ) {
39
+ /**
40
+ * A thunk that cancels specific working period payment, reloads WP data
41
+ * and updates store's state after certain delay.
42
+ *
43
+ * @param {string } periodId working period id
44
+ * @param {string } paymentId working period's payment id
45
+ * @param {number } [periodUpdateDelay] update delay for period data
46
+ * @returns {function }
47
+ */
48
+ export const cancelWorkPeriodPayment =
49
+ ( periodId , paymentId , periodUpdateDelay = SERVER_DATA_UPDATE_DELAY ) =>
50
+ async ( dispatch ) => {
51
+ let paymentData = null ;
52
+ let errorMessage = null ;
53
+ try {
54
+ paymentData = await services . cancelWorkPeriodPayment ( paymentId ) ;
55
+ paymentData = normalizePaymentData ( paymentData ) ;
56
+ } catch ( error ) {
51
57
errorMessage = error . toString ( ) ;
52
58
}
53
- }
54
- if ( periodData ) {
55
- dispatch ( actions . setWorkPeriodDataSuccess ( periodId , periodData ) ) ;
56
- return [ { ...periodData , userHandle } , null ] ;
57
- } else if ( errorMessage ) {
58
- dispatch ( actions . setWorkPeriodDataError ( periodId , errorMessage ) ) ;
59
- return [ null , errorMessage ] ;
60
- }
61
- return [ null , null ] ;
62
- } ;
63
-
64
- export const loadWorkPeriodAfterPaymentCancel =
65
- ( periodId , paymentId ) => async ( dispatch ) => {
66
- let [ periodData , error ] = await dispatch ( loadWorkPeriodData ( periodId ) ) ;
67
- if ( periodData ) {
59
+ if ( errorMessage ) {
60
+ makeToast ( errorMessage ) ;
61
+ return false ;
62
+ }
63
+ dispatch ( actions . setWorkPeriodPaymentData ( paymentData ) ) ;
64
+ let periodData ;
65
+ [ periodData , errorMessage ] = await dispatch (
66
+ loadWorkPeriodData ( periodId , periodUpdateDelay )
67
+ ) ;
68
+ if ( errorMessage ) {
69
+ makeToast ( "Failed to reload working period data. " + errorMessage ) ;
70
+ } else if ( periodData ) {
68
71
let userHandle = periodData . userHandle ;
69
72
let amount = null ;
70
73
for ( let payment of periodData . payments ) {
@@ -77,9 +80,47 @@ export const loadWorkPeriodAfterPaymentCancel =
77
80
`Payment ${ amount } for ${ userHandle } was marked as "cancelled"` ,
78
81
"success"
79
82
) ;
80
- } else if ( error ) {
81
- makeToast ( "Failed to reload working period data. " + error ) ;
82
83
}
84
+ return true ;
85
+ } ;
86
+
87
+ /**
88
+ * A thunk that loads specific working period data and updates store's state.
89
+ *
90
+ * @param {string } periodId working period id
91
+ * @param {number } [updateDelay] update delay in milliseconds
92
+ * @returns {function }
93
+ */
94
+ export const loadWorkPeriodData =
95
+ ( periodId , updateDelay = 0 ) =>
96
+ async ( dispatch , getState ) => {
97
+ if ( updateDelay > 0 ) {
98
+ await delay ( updateDelay ) ;
99
+ }
100
+ let [ periodsData ] = selectors . getWorkPeriodsData ( getState ( ) ) ;
101
+ periodsData [ periodId ] ?. cancelSource ?. cancel ( ) ;
102
+ const [ promise , source ] = services . fetchWorkPeriod ( periodId ) ;
103
+ dispatch ( actions . setWorkPeriodDataPending ( periodId , source ) ) ;
104
+ let userHandle = null ;
105
+ let periodData = null ;
106
+ let errorMessage = null ;
107
+ try {
108
+ const data = await promise ;
109
+ userHandle = data . userHandle ;
110
+ periodData = normalizePeriodData ( data ) ;
111
+ } catch ( error ) {
112
+ if ( ! axios . isCancel ( error ) ) {
113
+ errorMessage = error . toString ( ) ;
114
+ }
115
+ }
116
+ if ( periodData ) {
117
+ dispatch ( actions . setWorkPeriodDataSuccess ( periodId , periodData ) ) ;
118
+ return [ { ...periodData , userHandle } , null ] ;
119
+ } else if ( errorMessage ) {
120
+ dispatch ( actions . setWorkPeriodDataError ( periodId , errorMessage ) ) ;
121
+ return [ null , errorMessage ] ;
122
+ }
123
+ return [ null , null ] ;
83
124
} ;
84
125
85
126
/**
@@ -254,17 +295,14 @@ export const toggleWorkPeriodDetails =
254
295
*
255
296
* @param {string } periodId working period id
256
297
* @param {number } billingAccountId desired billing account id
257
- * @param {number } [periodDataDelay] timeout after which the period data gets
258
- * reloaded
259
298
* @returns {function }
260
299
*/
261
300
export const updatePaymentsBillingAccount =
262
- ( periodId , billingAccountId , periodDataDelay = 3000 ) =>
263
- async ( dispatch , getState ) => {
301
+ ( periodId , billingAccountId ) => async ( dispatch , getState ) => {
264
302
let [ periodsData ] = selectors . getWorkPeriodsData ( getState ( ) ) ;
265
303
let periodData = periodsData [ periodId ] ;
266
304
if ( ! periodData ) {
267
- return ;
305
+ return true ; // no period to update
268
306
}
269
307
let paymentsToUpdate = [ ] ;
270
308
for ( let payment of periodData . payments ) {
@@ -290,18 +328,35 @@ export const updatePaymentsBillingAccount =
290
328
makeToast ( errorMessage ) ;
291
329
return false ;
292
330
}
293
- await delay ( periodDataDelay ) ;
294
- [ periodData , errorMessage ] = await dispatch ( loadWorkPeriodData ( periodId ) ) ;
295
- if ( errorMessage ) {
296
- makeToast ( "Failed to reload payments' data. " + errorMessage ) ;
297
- } else if ( periodData ) {
298
- paymentsData = periodData . payments ;
299
- }
300
331
let paymentsNotUpdated = [ ] ;
332
+ let paymentsUpdated = new Map ( ) ;
301
333
for ( let payment of paymentsData ) {
302
- if ( payment . billingAccountId !== billingAccountId ) {
334
+ if ( "error" in payment || payment . billingAccountId !== billingAccountId ) {
303
335
paymentsNotUpdated . push ( payment ) ;
336
+ } else {
337
+ paymentsUpdated . set ( payment . id , payment ) ;
338
+ }
339
+ }
340
+ periodData = periodsData [ periodId ] ;
341
+ if ( ! periodData ) {
342
+ return true ; // no period to update
343
+ }
344
+ if ( paymentsUpdated . size ) {
345
+ let payments = [ ] ;
346
+ let paymentsOld = periodData . payments ;
347
+ for ( let i = 0 , len = paymentsOld . length ; i < len ; i ++ ) {
348
+ let paymentOld = paymentsOld [ i ] ;
349
+ if ( paymentsUpdated . has ( paymentOld . id ) ) {
350
+ // We update only billingAccountId because other payment properties
351
+ // may have been updated on the server and as a result the UI state
352
+ // may become inconsistent, i.e. WP properties like status and
353
+ // total paid may become inconsisten with payments' properties.
354
+ payments . push ( { ...paymentOld , billingAccountId } ) ;
355
+ } else {
356
+ payments . push ( paymentOld ) ;
357
+ }
304
358
}
359
+ dispatch ( actions . setWorkPeriodPayments ( periodId , payments ) ) ;
305
360
}
306
361
if ( paymentsNotUpdated . length ) {
307
362
makeToast ( "Could not update billing account for some payments." ) ;
0 commit comments