1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

mod functions;
pub mod migration;
mod types;
#[frame_support::pallet]
pub mod pallet {
  use frame_support::{
    pallet_prelude::{ValueQuery, *},
    traits::{Currency, Time},
    BoundedVec,
  };
  use frame_system::pallet_prelude::*;
  use scale_info::prelude::vec;
  use sp_runtime::sp_std::vec::Vec;
  use sp_runtime::traits::Scale;

  const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
  use crate::types::*;
  use pallet_rbac::types::RoleBasedAccessControl;
  pub type BalanceOf<T> =
    <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

  #[pallet::config]
  pub trait Config: frame_system::Config {
    type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

    type Moment: Parameter
      + Default
      + Scale<Self::BlockNumber, Output = Self::Moment>
      + Copy
      + MaxEncodedLen
      + scale_info::StaticTypeInfo
      + Into<u64>;

    type Timestamp: Time<Moment = Self::Moment>;

    type Rbac: RoleBasedAccessControl<Self::AccountId>;

    type RemoveOrigin: EnsureOrigin<Self::RuntimeOrigin>;

    type Currency: Currency<Self::AccountId>;

    #[pallet::constant]
    type MaxDocuments: Get<u32>;

    #[pallet::constant]
    type MaxProjectsPerUser: Get<u32>;

    #[pallet::constant]
    type MaxUserPerProject: Get<u32>;

    #[pallet::constant]
    type MaxBuildersPerProject: Get<u32>;

    #[pallet::constant]
    type MaxInvestorsPerProject: Get<u32>;

    #[pallet::constant]
    type MaxIssuersPerProject: Get<u32>;

    #[pallet::constant]
    type MaxRegionalCenterPerProject: Get<u32>;

    #[pallet::constant]
    type MaxDrawdownsPerProject: Get<u32>;

    #[pallet::constant]
    type MaxTransactionsPerDrawdown: Get<u32>;

    #[pallet::constant]
    type MaxRegistrationsAtTime: Get<u32>;

    #[pallet::constant]
    type MaxExpendituresPerProject: Get<u32>;

    #[pallet::constant]
    type MaxProjectsPerInvestor: Get<u32>;

    #[pallet::constant]
    type MaxBanksPerProject: Get<u32>;

    #[pallet::constant]
    type MaxJobEligiblesByProject: Get<u32>;

    #[pallet::constant]
    type MaxRevenuesByProject: Get<u32>;

    #[pallet::constant]
    type MaxTransactionsPerRevenue: Get<u32>;

    #[pallet::constant]
    type MaxStatusChangesPerDrawdown: Get<u32>;

    #[pallet::constant]
    type MaxStatusChangesPerRevenue: Get<u32>;

    #[pallet::constant]
    type MaxRecoveryChanges: Get<u32>;

    #[pallet::constant]
    type MinAdminBalance: Get<BalanceOf<Self>>;

    #[pallet::constant]
    type TransferAmount: Get<BalanceOf<Self>>;
  }

  #[pallet::pallet]
  #[pallet::storage_version(STORAGE_VERSION)]
  #[pallet::generate_store(pub(super) trait Store)]
  pub struct Pallet<T>(_);

  /* --- Onchain storage section --- */

  #[pallet::storage]
  #[pallet::getter(fn global_scope)]
  pub(super) type GlobalScope<T> = StorageValue<
    _,
    [u8; 32], // Value global scope id
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn users_info)]
  pub(super) type UsersInfo<T: Config> = StorageMap<
    _,
    Blake2_128Concat,
    T::AccountId, // Key account_id
    UserData<T>,  // Value UserData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn projects_info)]
  pub(super) type ProjectsInfo<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,      // Key project_id
    ProjectData<T>, // Value ProjectData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn users_by_project)]
  pub(super) type UsersByProject<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,                                      // Key project_id
    BoundedVec<T::AccountId, T::MaxUserPerProject>, // Value users
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn projects_by_user)]
  pub(super) type ProjectsByUser<T: Config> = StorageMap<
    _,
    Blake2_128Concat,
    T::AccountId,                                // Key account_id
    BoundedVec<[u8; 32], T::MaxProjectsPerUser>, // Value projects
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn expenditures_info)]
  pub(super) type ExpendituresInfo<T: Config> = StorageMap<
    _,
    Identity,
    ExpenditureId,   // Key expenditure_id
    ExpenditureData, // Value ExpenditureData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn expenditures_by_project)]
  pub(super) type ExpendituresByProject<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,                                          // Key project_id
    BoundedVec<[u8; 32], T::MaxExpendituresPerProject>, // Value expenditures
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn drawdowns_info)]
  pub(super) type DrawdownsInfo<T: Config> = StorageMap<
    _,
    Identity,
    DrawdownId,      // Key drawdown id
    DrawdownData<T>, // Value DrawdownData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn drawdowns_by_project)]
  pub(super) type DrawdownsByProject<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,                                         // Key project_id
    BoundedVec<DrawdownId, T::MaxDrawdownsPerProject>, // Value Drawdowns
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn transactions_info)]
  pub(super) type TransactionsInfo<T: Config> = StorageMap<
    _,
    Identity,
    TransactionId,      // Key transaction id
    TransactionData<T>, // Value TransactionData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn transactions_by_drawdown)]
  pub(super) type TransactionsByDrawdown<T: Config> = StorageDoubleMap<
    _,
    Identity,
    ProjectId, //K1: project id
    Identity,
    DrawdownId,                                               //K2: drawdown id
    BoundedVec<TransactionId, T::MaxTransactionsPerDrawdown>, // Value transactions
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn job_eligibles_info)]
  pub(super) type JobEligiblesInfo<T: Config> = StorageMap<
    _,
    Identity,
    JobEligibleId,   // Key transaction id
    JobEligibleData, // Value JobEligibleData
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn job_eligibles_by_project)]
  pub(super) type JobEligiblesByProject<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,                                              // Key project_id
    BoundedVec<JobEligibleId, T::MaxJobEligiblesByProject>, // Value job eligibles
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn revenues_info)]
  pub(super) type RevenuesInfo<T: Config> = StorageMap<
    _,
    Identity,
    RevenueId,      // Key revenue id
    RevenueData<T>, // Value RevenueData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn revenues_by_project)]
  pub(super) type RevenuesByProject<T: Config> = StorageMap<
    _,
    Identity,
    ProjectId,                                        // Key project_id
    BoundedVec<RevenueId, T::MaxDrawdownsPerProject>, // Value Revenues
    ValueQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn revenue_transactions_info)]
  pub(super) type RevenueTransactionsInfo<T: Config> = StorageMap<
    _,
    Identity,
    RevenueTransactionId,      // Key revenue transaction id
    RevenueTransactionData<T>, // Value RevenueTransactionData<T>
    OptionQuery,
  >;

  #[pallet::storage]
  #[pallet::getter(fn transactions_by_revenue)]
  pub(super) type TransactionsByRevenue<T: Config> = StorageDoubleMap<
    _,
    Identity,
    ProjectId, //K1: project id
    Identity,
    RevenueId,                                                      //K2: revenue id
    BoundedVec<RevenueTransactionId, T::MaxTransactionsPerRevenue>, // Value revenue transactions
    ValueQuery,
  >;

  // E V E N T S
  // ------------------------------------------------------------------------------------------------------------

  #[pallet::event]
  #[pallet::generate_deposit(pub(super) fn deposit_event)]
  pub enum Event<T: Config> {
    /// Proxy initial setup completed using the sudo pallet
    ProxySetupCompleted,
    /// Project was created successfully
    ProjectCreated(T::AccountId, ProjectId),
    /// The selected roject was edited successfully
    ProjectEdited(T::AccountId, ProjectId),
    /// The selected project was deleted successfully
    ProjectDeleted(T::AccountId, ProjectId),
    /// Administrator was registered successfully using the sudo pallet
    AdministratorAssigned(T::AccountId),
    /// Administrator was removed successfully using the sudo pallet
    AdministratorRemoved(T::AccountId),
    /// The user was assigned to the selected project
    UserAssignmentCompleted(T::AccountId, ProjectId),
    /// The user was unassigned to the selected project
    UserUnassignmentCompleted(T::AccountId, ProjectId),
    /// Users extrinsic was executed, individual CUDActions were applied
    UsersExecuted(T::AccountId),
    /// A new user account was created successfully
    UserCreated(T::AccountId),
    /// The selected user was edited successfully
    UserUpdated(T::AccountId),
    /// The selected user was deleted successfully
    UserDeleted(T::AccountId),
    /// An array of expenditures was executed depending on the CUDAction
    ExpendituresExecuted(T::AccountId, ProjectId),
    /// Expenditure was created successfully
    ExpenditureCreated(ProjectId, ExpenditureId),
    /// Expenditure was updated successfully
    ExpenditureUpdated(ProjectId, ExpenditureId),
    /// Expenditure was deleted successfully
    ExpenditureDeleted(ProjectId, ExpenditureId),
    /// An array of transactions was executed depending on the CUDAction
    TransactionsExecuted(ProjectId, DrawdownId),
    /// Transaction was created successfully
    TransactionCreated(ProjectId, DrawdownId, TransactionId),
    /// Transaction was edited successfully
    TransactionEdited(ProjectId, DrawdownId, TransactionId),
    /// Transaction was deleted successfully
    TransactionDeleted(ProjectId, DrawdownId, TransactionId),
    /// Assign users extrinsic was completed successfully
    UsersAssignationExecuted(T::AccountId, ProjectId),
    /// Drawdowns were initialized successfully at the beginning of the project
    DrawdownsInitialized(T::AccountId, ProjectId),
    /// Drawdown was created successfully
    DrawdownCreated(ProjectId, DrawdownId),
    /// Drawdown was submitted successfully
    DrawdownSubmitted(ProjectId, DrawdownId),
    /// Drawdown was approved successfully
    DrawdownApproved(ProjectId, DrawdownId),
    /// Drawdown was rejected successfully
    DrawdownRejected(ProjectId, DrawdownId),
    /// Drawdown was cancelled successfully
    DrawdownSubmissionCancelled(ProjectId, DrawdownId),
    /// Bulkupload drawdown was submitted successfully
    BulkUploadSubmitted(ProjectId, DrawdownId),
    /// An array of adjustments was executed depending on the CUDAction
    InflationRateAdjusted(T::AccountId),
    /// An array of job eligibles was executed depending on the CUDAction
    JobEligiblesExecuted(T::AccountId, ProjectId),
    /// Job eligible was created successfully
    JobEligibleCreated(ProjectId, JobEligibleId),
    /// Job eligible was updated successfully
    JobEligibleUpdated(ProjectId, JobEligibleId),
    /// Job eligible was deleted successfully
    JobEligibleDeleted(ProjectId, JobEligibleId),
    /// Revenue transaction was created successfully
    RevenueTransactionCreated(ProjectId, RevenueId, RevenueTransactionId),
    /// Revenue transaction was updated successfully
    RevenueTransactionUpdated(ProjectId, RevenueId, RevenueTransactionId),
    /// Revenue transaction was deleted successfully
    RevenueTransactionDeleted(ProjectId, RevenueId, RevenueTransactionId),
    /// An array of revenue transactions was executed depending on the CUDAction
    RevenueTransactionsExecuted(ProjectId, RevenueId),
    /// Revenue was created successfully
    RevenueCreated(ProjectId, RevenueId),
    /// Revenue was submitted successfully
    RevenueSubmitted(ProjectId, RevenueId),
    /// Revenue was approved successfully
    RevenueApproved(ProjectId, RevenueId),
    /// Revenue was rejected successfully
    RevenueRejected(ProjectId, RevenueId),
    /// Bank's confirming documents were uploaded successfully
    BankDocumentsUploaded(ProjectId, DrawdownId),
    /// Bank's confirming documents were updated successfully
    BankDocumentsUpdated(ProjectId, DrawdownId),
    /// Bank's confirming documents were deleted successfully
    BankDocumentsDeleted(ProjectId, DrawdownId),
    /// Error recovery for revenues was executed successfully
    RevenueErrorRecoveryExecuted(ProjectId, RevenueId),
    /// Error recovery for drawdowns was executed successfully
    DrawdownErrorRecoveryExecuted(ProjectId, DrawdownId),
  }

  // E R R O R S
  // ------------------------------------------------------------------------------------------------------------
  #[pallet::error]
  pub enum Error<T> {
    /// FieldName is empty
    EmptyFieldName,
    /// FieldDescription is empty
    EmptyFieldDescription,
    /// FieldName is too long
    FieldNameTooLong,
    /// Array of users is empty
    EmptyUsers,
    /// CID is empty
    EmptyFieldCID,
    /// Array of banks is empty
    EmptyFieldBanks,
    /// The private group id is empty
    PrivateGroupIdEmpty,
    /// Array of users to be assigned to a project is empty
    EmptyUsersAssignation,
    /// Field address project is empty
    EmptyProjectAddress,
    /// No value was found for the global scope
    NoGlobalScopeValueWasFound,
    /// Project ID is already in use
    ProjectIdAlreadyInUse,
    /// Timestamp was not genereated correctly
    TimestampError,
    /// Completion date must be later than creation date
    CompletionDateMustBeLater,
    /// User is already registered in the site
    UserAlreadyRegistered,
    /// Project was not found
    ProjectNotFound,
    /// Project is not active anymore
    ProjectIsAlreadyCompleted,
    /// Project has no drawdowns
    ProjectHasNoDrawdowns,
    /// Project has no expenditures
    ProjectHasNoExpenditures,
    /// Project has no users
    ProjectHasNoUsers,
    /// Project has no job eligibles
    ProjectHasNoJobEligibles,
    /// Project has no revenues
    ProjectHasNoRevenues,
    /// Can not delete a completed project
    CannotDeleteCompletedProject,
    /// User is not registered
    UserNotRegistered,
    /// User has been already added to the project
    UserAlreadyAssignedToProject,
    /// Max number of users per project reached
    MaxUsersPerProjectReached,
    /// Max number of projects per user reached
    MaxProjectsPerUserReached,
    /// User is not assigned to the project
    UserNotAssignedToProject,
    /// Can not register administrator role
    CannotRegisterAdminRole,
    /// Max number of builders per project reached
    MaxBuildersPerProjectReached,
    /// Max number of investors per project reached
    MaxInvestorsPerProjectReached,
    /// Max number of issuers per project reached
    MaxIssuersPerProjectReached,
    /// Max number of regional centers per project reached
    MaxRegionalCenterPerProjectReached,
    /// Can not remove administrator role
    CannotRemoveAdminRole,
    /// Can not add admin role at user project assignment
    CannotAddAdminRole,
    /// User can not have more than one role at the same time
    UserCannotHaveMoreThanOneRole,
    /// Expenditure not found
    ExpenditureNotFound,
    /// Expenditure not found for the selected project_id
    ExpenditureNotFoundForSelectedProjectId,
    /// Expenditure already exist
    ExpenditureAlreadyExists,
    /// Expenditure is already in a transaction
    ExpenditureHasNonZeroTransactions,
    /// Max number of expenditures per project reached
    MaxExpendituresPerProjectReached,
    /// Field name can not be empty
    EmptyExpenditureName,
    /// Expenditure does not belong to the project
    ExpenditureDoesNotBelongToProject,
    /// Drawdown id is not found
    DrawdownNotFound,
    /// Invalid amount
    InvalidAmount,
    /// Documents field is empty
    DocumentsEmpty,
    /// Transaction id is not found
    TransactionNotFound,
    /// Transaction was not found for the selected Drawdown_id
    TransactionNotFoundForSelectedDrawdownId,
    /// Transaction already exist
    TransactionAlreadyExists,
    /// Transaction is already in a drawdown
    TransactionInUse,
    /// Max number of transactions per drawdown reached
    MaxTransactionsPerDrawdownReached,
    /// Drawdown already exist
    DrawdownAlreadyExists,
    /// Max number of drawdowns per project reached
    MaxDrawdownsPerProjectReached,
    /// Max number of status changes per drawdown reached
    MaxStatusChangesPerDrawdownReached,
    /// Max number of recovery chnages per drawdown reached
    MaxRecoveryChangesReached,
    /// Can not modify a completed drawdown
    CannotEditDrawdown,
    /// Can not perform any action on a submitted transaction
    CannotPerformActionOnSubmittedTransaction,
    /// Can not perform any action on a approved transaction
    CannotPerformActionOnApprovedTransaction,
    /// Can not perform any action on a confirmed transaction
    CannotPerformActionOnConfirmedTransaction,
    /// Can not perform any action on a submitted drawdown
    CannotPerformActionOnSubmittedDrawdown,
    /// Can not perform any action on a approved drawdown
    CannotPerformActionOnApprovedDrawdown,
    /// Can not perform any action on a confirmed drawdown
    CannotPerformActionOnConfirmedDrawdown,
    /// Transaction is already completed
    TransactionIsAlreadyCompleted,
    /// User does not have the specified role
    UserDoesNotHaveRole,
    /// Transactions vector is empty
    EmptyTransactions,
    /// Transactions are required for the current workflow
    TransactionsRequired,
    /// Transaction ID was not found in do_execute_transaction
    TransactionIdRequired,
    /// Drawdown can not be submitted if does not has any transactions
    DrawdownHasNoTransactions,
    /// Cannot submit transaction
    CannotSubmitTransaction,
    /// Drawdown can not be approved if is not in submitted status
    DrawdownIsNotInSubmittedStatus,
    /// Transactions is not in submitted status
    TransactionIsNotInSubmittedStatus,
    /// Array of expenditures is empty
    EmptyExpenditures,
    /// Expenditure name is required
    ExpenditureNameRequired,
    /// Expenditure type is required
    ExpenditureTypeRequired,
    /// Expenditure amount is required
    ExpenditureAmountRequired,
    /// Expenditure id is required
    ExpenditureIdRequired,
    /// User name is required
    UserNameRequired,
    /// User role is required
    UserRoleRequired,
    /// User image is required
    UserImageRequired,
    /// User email is required
    UserEmailRequired,
    /// Amount is required
    AmountRequired,
    /// Can not delete a user if the user is assigned to a project
    UserHasAssignedProjects,
    /// User has no projects assigned
    UserHasNoProjects,
    /// Can not send a drawdown to submitted status if it has no transactions
    NoTransactionsToSubmit,
    /// Bulk upload description is required
    BulkUploadDescriptionRequired,
    /// Bulk upload documents are required
    BulkUploadDocumentsRequired,
    /// Administrator can not delete themselves
    AdministratorsCannotDeleteThemselves,
    /// No feedback was provided for bulk upload
    NoFeedbackProvidedForBulkUpload,
    /// Bulkupload feedback is empty
    EmptyBulkUploadFeedback,
    /// NO feedback for EN5 drawdown was provided
    EB5MissingFeedback,
    /// EB5 feedback is empty
    EmptyEb5Feedback,
    /// Inflation rate extrinsic is missing an array of project ids
    ProjectsInflationRateEmpty,
    /// Inflation rate was not provided
    InflationRateRequired,
    /// Inflation rate has been already set for the selected project
    InflationRateAlreadySet,
    /// Inflation rate was not set for the selected project
    InflationRateNotSet,
    /// Bulkupload drawdowns are only allowed for Construction Loan & Developer Equity
    DrawdownTypeNotSupportedForBulkUpload,
    /// Cannot edit user role if the user is assigned to a project
    UserHasAssignedProjectsCannotUpdateRole,
    /// Cannot delete user if the user is assigned to a project
    UserHasAssignedProjectsCannotDelete,
    /// Cannot send a bulkupload drawdown if the drawdown status isn't in draft or rejected
    DrawdownStatusNotSupportedForBulkUpload,
    /// Cannot submit a drawdown if the drawdown status isn't in draft or rejected
    DrawdownIsNotInDraftOrRejectedStatus,
    /// Only investors can update/edit their documents
    UserIsNotAnInvestor,
    /// Max number of projects per investor has been reached
    MaxProjectsPerInvestorReached,
    /// Jobs eligibles array is empty
    JobEligiblesEmpty,
    /// JOb eligible name is empty
    JobEligiblesNameRequired,
    /// Job eligible id already exists
    JobEligibleIdAlreadyExists,
    /// Max number of job eligibles per project reached
    MaxJobEligiblesPerProjectReached,
    /// Job eligible id not found
    JobEligibleNotFound,
    /// Jopb eligible does not belong to the project
    JobEligibleDoesNotBelongToProject,
    /// Job eligible name is required
    JobEligibleNameRequired,
    /// Job eligible amount is required
    JobEligibleAmountRequired,
    /// Job eligible id is required
    JobEligibleIdRequired,
    /// Job eligible not found for the given project id
    JobEligibleNotFoundForSelectedProjectId,
    /// Job eligible has non zero transactions
    JobEligibleHasNonZeroTransactions,
    /// Revenue id was not found
    RevenueNotFound,
    /// Transactions revenue array is empty
    RevenueTransactionsEmpty,
    /// An array of revenue transactions is required
    RevenueTransactionsRequired,
    /// Revenue transaction is not in submitted status
    RevenueTransactionNotSubmitted,
    /// Revenue can not be edited
    CannotEditRevenue,
    /// Revenue transaction id already exists
    RevenueTransactionIdAlreadyExists,
    /// Max number of transactions per revenue reached
    MaxTransactionsPerRevenueReached,
    /// Revenue transaction id not found
    RevenueTransactionNotFound,
    /// Revenue transaction was not found for the selected revenue_id
    RevenueTransactionNotFoundForSelectedRevenueId,
    /// Revenue transaction can not be edited
    CannotEditRevenueTransaction,
    /// Max number of status changes per revenue reached
    MaxStatusChangesPerRevenueReached,
    /// Can not perform any action on a submitted revenue
    CannotPerformActionOnSubmittedRevenue,
    /// Can not perform any action on a approved revenue
    CannotPerformActionOnApprovedRevenue,
    /// Can not perform any action on a submitted revenue transaction
    CannotPerformActionOnApprovedRevenueTransaction,
    /// Can not perform any action on a approved revenue transaction
    CannotPerformActionOnSubmittedRevenueTransaction,
    /// Revenue amoun is required
    RevenueAmountRequired,
    /// Revenue transaction id is required
    RevenueTransactionIdRequired,
    /// Revenue Id already exists
    RevenueIdAlreadyExists,
    /// Maximun number of revenues per project reached
    MaxRevenuesPerProjectReached,
    /// Can not send a revenue to submitted status if it has no transactions
    RevenueHasNoTransactions,
    /// Revenue is not in submitted status
    RevenueIsNotInSubmittedStatus,
    /// Revenue transaction is not in submitted status
    RevenueTransactionIsNotInSubmittedStatus,
    /// Revenue transactions feedback is empty
    RevenueTransactionsFeedbackEmpty,
    /// The revenue is not in submitted status
    RevenueNotSubmitted,
    /// The revenue id does not belong to the project
    RevenueDoesNotBelongToProject,
    /// Can not upload bank confirming documents if the drawdown is not in Approved status
    DrawdowMustBeInApprovedStatus,
    /// Drawdown is not in Confirmed status
    DrawdowMustBeInConfirmedStatus,
    /// Drawdown is not in Submitted status
    DrawdownNotSubmitted,
    /// Can not insert (CUDAction: Create) bank confmirng documents if the drawdown has already bank confirming documents
    DrawdownHasAlreadyBankConfirmingDocuments,
    /// Drawdown has no bank confirming documents (CUDAction: Update or Delete)
    DrawdownHasNoBankConfirmingDocuments,
    /// Drawdown id does not belong to the selected project
    DrawdownDoesNotBelongToProject,
    /// Bank confirming documents are required
    BankConfirmingDocumentsNotProvided,
    /// Banck confirming documents array is empty
    BankConfirmingDocumentsEmpty,
    /// Only eb5 drawdowns are allowed to upload bank documentation
    OnlyEB5DrawdownsCanUploadBankDocuments,
    /// Maximun number of registrations at a time reached
    MaxRegistrationsAtATimeReached,
    /// Administrator account has insuficiente balance to register a new user
    AdminHasNoFreeBalance,
    /// Administrator account has insuficiente balance to register a new user
    InsufficientFundsToTransfer,
  }

  // E X T R I N S I C S
  // ------------------------------------------------------------------------------------------------------------
  #[pallet::call]
  impl<T: Config> Pallet<T> {
    // I N I T I A L
    // --------------------------------------------------------------------------------------------
    /// Initialize the pallet by setting the permissions for each role
    /// & the global scope
    ///
    /// # Considerations:
    /// - This function can only be called once
    /// - This function can only be called usinf the sudo pallet
    #[pallet::call_index(1)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn initial_setup(origin: OriginFor<T>) -> DispatchResult {
      T::RemoveOrigin::ensure_origin(origin.clone())?;
      Self::do_initial_setup()?;
      Ok(())
    }

    /// Adds an administrator account to the site
    ///
    /// # Parameters:
    /// - origin: The sudo account
    /// - admin: The administrator account to be added
    /// - name: The name of the administrator account
    ///
    /// # Considerations:
    /// - This function can only be called using the sudo pallet
    /// - This function is used to add the first administrator to the site
    /// - If the user is already registered, the function will return an error: UserAlreadyRegistered
    /// - This function grants administrator permissions to the user from the rbac pallet
    /// - administrator role have global scope permissions
    #[pallet::call_index(2)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn sudo_add_administrator(
      origin: OriginFor<T>,
      admin: T::AccountId,
      name: FieldName,
    ) -> DispatchResult {
      T::RemoveOrigin::ensure_origin(origin.clone())?;
      Self::do_sudo_add_administrator(admin, name)?;
      Ok(())
    }

    /// Removes an administrator account from the site
    ///
    /// # Parameters:
    /// - origin: The sudo account
    /// - admin: The administrator account to be removed
    ///
    /// # Considerations:
    /// - This function can only be called using the sudo pallet
    /// - This function is used to remove any administrator from the site
    /// - If the user is not registered, the function will return an error: UserNotFound
    /// - This function removes administrator permissions of the user from the rbac pallet
    ///
    /// # Note:
    /// WARNING: Administrators can remove themselves from the site,
    /// but they can add themselves back
    #[pallet::call_index(3)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn sudo_remove_administrator(origin: OriginFor<T>, admin: T::AccountId) -> DispatchResult {
      T::RemoveOrigin::ensure_origin(origin.clone())?;
      Self::do_sudo_remove_administrator(admin)?;
      Ok(())
    }

    // U S E R S
    // --------------------------------------------------------------------------------------------
    /// This extrinsic is used to create, update, or delete a user account
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - user: The target user account to be registered, updated, or deleted.
    /// It is an array of user accounts where each entry it should be a tuple of the following:
    /// - 0: The user account
    /// - 1: The user name
    /// - 2: The user role
    /// - 3: The CUD operation to be performed on the user account. CUD action is ALWAYS
    ///   required.
    ///
    /// # Considerations:
    /// - Users parameters are optional because depends on the CUD action as follows:
    /// * **Create**: The user account, user name, user role & CUD action are required
    /// * **Update**: The user account & CUD action are required. The user name & user role are
    ///   optionals.
    /// * **Delete**: The user account & CUD action are required.
    /// - This function can only be called by an administrator account
    /// - Multiple users can be registered, updated, or deleted at the same time, but
    /// the user account must be unique. Multiple actions over the same user account
    /// in the same call, it could result in an unexpected behavior.
    /// - If the user is already registered, the function will return an error:
    ///   UserAlreadyRegistered
    /// - If the user is not registered, the function will return an error: UserNotFound
    ///
    /// # Note:
    /// - WARNING: It is possible to register, update, or delete administrators accounts using
    ///   this extrinsic,
    /// but administrators can not delete themselves.
    /// - WARNING: This function only registers, updates, or deletes users from the site.
    /// - WARNING: The only way to grant or remove permissions of a user account is assigning or
    ///   unassigning
    /// a user from a selected project.
    #[pallet::call_index(4)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn users(origin: OriginFor<T>, users: Users<T>) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_execute_users(who, users)
    }

    /// Edits an user account
    ///
    /// # Parameters:
    /// - origin: The user account which is being edited
    /// - name: The name of the user account which is being edited
    /// - image: The image of the user account which is being edited
    /// - email: The email of the user account which is being edited
    /// - documents: The documents of the user account which is being edited.
    /// ONLY available for the investor role.
    ///
    /// # Considerations:
    /// - If the user is not registered, the function will return an error: UserNotFound
    /// - This function can only be called by a registered user account
    /// - This function will be called by the user account itself
    /// - ALL parameters are optional because depends on what is being edited
    /// - ONLY the investor role can edit or update the documents
    #[pallet::call_index(5)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn users_edit_user(
      origin: OriginFor<T>,
      name: Option<FieldName>,
      image: Option<CID>,
      email: Option<FieldName>,
      documents: Option<Documents<T>>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_edit_user(who, name, image, email, documents)
    }

    // P R O J E C T S
    // --------------------------------------------------------------------------------------------
    /// Registers a new project.
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - title: The title of the project
    /// - description: The description of the project
    /// - image: The image of the project (CID)
    /// - address: The address of the project
    /// - creation_date: The creation date of the project
    /// - completion_date: The completion date of the project
    /// - expenditures: The expenditures of the project. It is an array of tuples where each
    ///   entry
    /// is a tuple of the following:
    /// * 0: The expenditure name
    /// * 1: The expenditure type
    /// * 2: The expenditure amount
    /// * 3: The expenditure NAICS code
    /// * 4: The expenditure jobs multiplier
    /// * 5: The CUD action to be performed on the expenditure. CUD action is ALWAYS required.
    /// * 6: The expenditure id. It is optional because it is only required when updating or
    ///   deleting
    /// - job_eligibles: The job eligibles to be created/updated/deleted. This is a vector of
    ///   tuples
    /// where each entry is composed by:
    /// * 0: The job eligible name
    /// * 1: The amount of the job eligible
    /// * 2: The NAICS code of the job eligible
    /// * 3: The jobs multiplier of the job eligible
    /// * 4: The job eligible action to be performed. (Create, Update or Delete)
    /// * 5: The job eligible id. This is only used when updating or deleting a job eligible.
    /// - users: The users who will be assigned to the project. It is an array of tuples where
    ///   each entry
    /// is a tuple of the following:
    /// * 0: The user account
    /// * 1: The user role
    /// * 2: The AssignAction to be performed on the user.
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - For users assignation, the user account must be registered. If the user is not
    ///   registered,
    /// the function will return an error. ALL parameters are required.
    /// - For expenditures, apart from the expenditure id, naics code & jopbs multiplier, ALL
    ///   parameters are required because for this
    /// flow, the expenditures are always created. The naics code & the jobs multiplier
    /// can be added later by the administrator.
    /// - Creating a project will automatically create a scope for the project.
    ///
    /// # Note:
    /// WARNING: If users are provided, the function will assign the users to the project,
    /// granting them permissions in the rbac pallet.
    #[pallet::call_index(6)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn projects_create_project(
      origin: OriginFor<T>,
      title: FieldName,
      description: FieldDescription,
      image: Option<CID>,
      address: FieldName,
      banks: Option<BoundedVec<(BankName, BankAddress), T::MaxBanksPerProject>>,
      creation_date: CreationDate,
      completion_date: CompletionDate,
      expenditures: Expenditures<T>,
      job_eligibles: Option<JobEligibles<T>>,
      users: Option<UsersAssignation<T>>,
      private_group_id: PrivateGroupId,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_create_project(
        who,
        title,
        description,
        image,
        address,
        banks,
        creation_date,
        completion_date,
        expenditures,
        job_eligibles,
        users,
        private_group_id,
      )
    }

    /// Edits a project.
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - project_id: The selected project id that will be edited
    /// - title: The title of the project to be edited
    /// - description: The description of the project to be edited
    /// - image: The image of the project to be edited
    /// - address: The address of the project to be edited
    /// - creation_date: The creation date of the project to be edited
    /// - completion_date: The completion date of the project to be edited
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - ALL parameters are optional because depends on what is being edited
    /// - The project id is required because it is the only way to identify the project
    /// - The project id must be registered. If the project is not registered,
    /// the function will return an error: ProjectNotFound
    /// - It is not possible to edit the expenditures or the users assigned to the project
    /// through this function. For that, the administrator must use the extrinsics:
    /// * expenditures
    /// * projects_assign_user
    /// - Project can only be edited in the Started status
    /// - Completion date must be greater than creation date
    #[pallet::call_index(7)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn projects_edit_project(
      origin: OriginFor<T>,
      project_id: ProjectId,
      title: Option<FieldName>,
      description: Option<FieldDescription>,
      image: Option<CID>,
      address: Option<FieldName>,
      banks: Option<BoundedVec<(BankName, BankAddress), T::MaxBanksPerProject>>,
      creation_date: Option<CreationDate>,
      completion_date: Option<CompletionDate>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_edit_project(
        who,
        project_id,
        title,
        description,
        image,
        address,
        banks,
        creation_date,
        completion_date,
      )
    }

    /// Deletes a project.
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - project_id: The selected project id that will be deleted
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - The project id is required because it is the only way to identify the project
    /// - The project id must be registered. If the project is not registered,
    /// the function will return an error: ProjectNotFound
    ///
    /// # Note:
    /// - WARNING: Deleting a project will also delete ALL stored information associated with
    ///   the project.
    /// BE CAREFUL.
    #[pallet::call_index(8)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn projects_delete_project(origin: OriginFor<T>, project_id: ProjectId) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_delete_project(who, project_id)
    }

    /// Assigns a user to a project.
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - project_id: The selected project id where user will be assigned
    /// - users: The users to be assigned to the project. This is a vector of tuples
    /// where each entry is composed by:
    /// * 0: The user account id
    /// * 1: The user role
    /// * 2: The AssignAction to be performed. (Assign or Unassign)
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - This extrinsic allows multiple users to be assigned/unassigned at the same time.
    /// - The project id is required because it is the only way to identify the project
    /// - This extrinsic is used for both assigning and unassigning users to a project
    /// depending on the AssignAction.
    /// - After a user is assigned to a project, the user will be able to perform actions
    /// in the project depending on the role assigned to the user.
    /// - After a user is unassigned from a project, the user will not be able to perform
    ///   actions
    /// in the project anymore.
    /// - If the user is already assigned to the project, the function will return an error.
    ///
    /// # Note:
    /// - WARNING: ALL provided users needs to be registered in the site. If any of the users
    /// is not registered, the function will return an error.
    /// - Assigning or unassigning a user to a project will add or remove permissions to the
    ///   user
    /// from the RBAC pallet.
    /// - Warning: Cannot assign a user to a project with a different role than the one they
    /// have in UsersInfo. If the user has a different role, the function will return an error.
    /// - Warning: Cannot unassign a user from a project with a different role than the one they
    /// have in UsersInfo. If the user has a different role, the function will return an error.
    /// - Warning: Do not perform multiple actions over the same user in the same call, it could
    /// result in an unexpected behavior.
    #[pallet::call_index(9)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn projects_assign_user(
      origin: OriginFor<T>,
      project_id: ProjectId,
      users: UsersAssignation<T>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_execute_assign_users(who, project_id, users)
    }

    // B U D G E T  E X P E N D I T U R E   &  J O B   E L I G I B L E S
    // --------------------------------------------------------------------------------------------
    /// This extrinsic is used to create, update or delete expenditures & job eligibles.
    ///
    /// # Parameters:
    /// - origin: The administrator account
    /// - project_id: The selected project id where the expenditures will be
    ///   created/updated/deleted
    /// - expenditures: The expenditures to be created/updated/deleted. This is a vector of
    ///   tuples
    /// where each entry is composed by:
    /// * 0: The name of the expenditure
    /// * 1: The expenditure type
    /// * 2: The amount of the expenditure
    /// * 3: The naics code of the expenditure
    /// * 4: The jobs multiplier of the expenditure
    /// * 5: The expenditure action to be performed. (Create, Update or Delete)
    /// * 6: The expenditure id. This is only used when updating or deleting an expenditure.
    /// - job_eligibles: The job eligibles to be created/updated/deleted. This is a vector of
    ///   tuples
    /// where each entry is composed by:
    /// * 0: The job eligible name
    /// * 1: The amount of the job eligible
    /// * 2: The NAICS code of the job eligible
    /// * 3: The jobs multiplier of the job eligible
    /// * 4: The job eligible action to be performed. (Create, Update or Delete)
    /// * 5: The job eligible id. This is only used when updating or deleting a job eligible.
    ///
    /// # Considerations:
    /// - Naics code and jobs multiplier are always optional.
    /// - This function can only be called by an administrator account
    /// - This extrinsic allows multiple expenditures to be created/updated/deleted at the same
    ///   time.
    /// - The project id is required because it is the only way to identify the project
    /// - Expenditure parameters are optional because depends on the action to be performed:
    /// * **Create**: Name, Type & Amount are required. Nacis code & Jobs multiplier are
    ///   optional.
    /// * **Update**: Except for the expenditure id & action, all parameters are optional.
    /// * **Delete**: Only the expenditure id & action is required.
    /// - Multiple actions can be performed at the same time. For example, you can create a new
    /// expenditure and update another one at the same time.
    /// - Do not perform multiple actions over the same expenditure in the same call, it could
    /// result in an unexpected behavior.
    #[pallet::call_index(10)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn expenditures_and_job_eligibles(
      origin: OriginFor<T>,
      project_id: ProjectId,
      expenditures: Option<Expenditures<T>>,
      job_eligibles: Option<JobEligibles<T>>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      if let Some(mod_expenditures) = expenditures {
        Self::do_execute_expenditures(who.clone(), project_id, mod_expenditures)?;
      }

      if let Some(mod_job_eligibles) = job_eligibles {
        Self::do_execute_job_eligibles(who, project_id, mod_job_eligibles)?;
      }

      Ok(())
    }

    // T R A N S A C T I O N S   &  D R A W D O W N S
    // --------------------------------------------------------------------------------------------

    /// Submit a drawdown
    /// This extrinsic is used to create, update or delete transactions.
    /// It also allows that an array of transactions to be saved as a draft or as submitted.
    ///
    /// # Parameters:
    /// - origin: The user account who is creating the transactions
    /// - project_id: The selected project id where the transactions will be created
    /// - drawdown_id: The selected drawdown id where the transactions will be created
    /// - transactions: The transactions to be created/updated/deleted. This entry is a vector
    ///   of tuples
    /// where each entry is composed by:
    /// * 0: The expenditure id where the transaction will be created
    /// * 1: The amount of the transaction
    /// * 2: Documents associated to the transaction
    /// * 3: The action to be performed on the transaction. (Create, Update or Delete)
    /// * 4: The transaction id. This is only used when updating or deleting a transaction.
    /// - submit: If true, transactions associated to the selected
    /// drawdown will be submitted to the administrator.
    /// If false, the array of transactions will be saved as a draft.
    ///
    /// # Considerations:
    /// - This function is only callable by a builder role account
    /// - This extrinsic allows multiple transactions to be created/updated/deleted at the same
    ///   time.
    /// - The project id and drawdown id are required for the reports.
    /// - Transaction parameters are optional because depends on the action to be performed:
    /// * **Create**: Expenditure id, Amount, Documents & action are required.
    /// * **Update**: Except for the transaction id & action, all other parameters are optional.
    /// * **Delete**: Only the transaction id & action are required.
    /// - Multiple actions can be performed at the same time, but each must be performed on
    /// a different transaction. For example, you can create a new
    /// transaction and update another one at the same time.
    /// - Do not perform multiple actions over the same transaction in the same call, it could
    /// result in an unexpected behavior.
    /// - If a drawdown is submitted, all transactions must be submitted too. If the drawdown do
    ///   not contain
    /// any transaction, it will return an error.
    /// - After a drawdown is submitted, it can not be updated or deleted.
    /// - After a drawdown is rejected, builders will use again this extrinsic to update the
    /// transactions associated to a given drawdown.
    #[pallet::call_index(11)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn submit_drawdown(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      transactions: Option<Transactions<T>>,
      submit: bool,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      match submit {
        // Save transactions as draft
        false => {
          // Do execute transactions
          Self::do_execute_transactions(
            who,
            project_id,
            drawdown_id,
            transactions.ok_or(Error::<T>::TransactionsRequired)?,
          )
        },
        // Submit transactions
        true => {
          // Check if there are transactions to execute
          if let Some(mod_transactions) = transactions {
            // Ensure transactions are not empty
            ensure!(!mod_transactions.is_empty(), Error::<T>::EmptyTransactions);

            // Do execute transactions
            Self::do_execute_transactions(who.clone(), project_id, drawdown_id, mod_transactions)?;
          }

          // Do submit drawdown
          Self::do_submit_drawdown(who, project_id, drawdown_id)
        },
      }
    }

    /// Approve a drawdown
    ///
    /// # Parameters:
    /// ### For EB5 drawdowns:
    /// - origin: The administrator account who is approving the drawdown
    /// - project_id: The selected project id where the drawdown will be approved
    /// - drawdown_id: The selected drawdown id to be approved
    ///
    /// ### For Construction Loan & Developer Equity (bulk uploads) drawdowns:
    /// - origin: The administrator account who is approving the drawdown
    /// - project_id: The selected project id where the drawdown will be approved
    /// - drawdown_id: The selected drawdown id to be approved.
    /// - bulkupload: Optional bulkupload parameter. If true, the drawdown will be saved in a
    ///   pseudo
    /// draft status. If false, the drawdown will be approved directly.
    /// - transactions: The transactions to be created/updated/deleted. This is a vector of
    ///   tuples
    /// where each entry is composed by:
    /// * 0: The expenditure id where the transaction will be created
    /// * 1: The transaction amount
    /// * 2: Documents associated to the transaction
    /// * 3: The transaction action to be performed. (Create, Update or Delete)
    /// * 4: The transaction id. This is only used when updating or deleting a transaction.
    /// - This extrinsic allows multiple transactions to be created/updated/deleted at the same
    ///   time
    /// (only for Construction Loan & Developer Equity drawdowns).
    /// - Transaction parameters are optional because depends on the action to be performed:
    /// * **Create**: Expenditure id, Amount, Documents & action are required.
    /// * **Update**: Except for the transaction id & action, all parameters are optional.
    /// * **Delete**: Only the transaction id & action are required.
    /// - Multiple actions can be performed at the same time. For example, you can create a new
    /// transaction and update another one at the same time (only for Construction Loan &
    /// Developer Equity drawdowns).
    /// - Do not perform multiple actions over the same transaction in the same call, it could
    /// result in an unexpected behavior (only for Construction Loan & Developer Equity
    /// drawdowns).
    ///
    /// # Considerations:
    /// - This function is only callable by an administrator account
    /// - All transactions associated to the drawdown will be approved too. It's
    /// not possible to approve a drawdown without approving all of its transactions.
    /// - After a drawdown is approved, it can not be updated or deleted.
    /// - After a drawdown is approved, the next drawdown will be automatically created.
    /// - The drawdown status will be updated to "Approved" after the extrinsic is executed.
    /// - After a drawdown is rejected, administrators will use again this extrinsic to approve
    ///   the
    /// new drawdown version uploaded by the builder.
    #[pallet::call_index(12)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn approve_drawdown(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      bulkupload: Option<bool>,
      transactions: Option<Transactions<T>>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      // Match bulkupload parameter
      match bulkupload {
        Some(approval) => {
          // Ensure admin permissions
          Self::is_authorized(who.clone(), &project_id, ProxyPermission::ApproveDrawdown)?;

          // Execute bulkupload flow (construction loan & developer equity)
          match approval {
            false => {
              // 1. Do execute transactions
              Self::do_execute_transactions(
                who.clone(),
                project_id,
                drawdown_id,
                transactions.ok_or(Error::<T>::TransactionsRequired)?,
              )?;

              // 2. Do submit drawdown
              Self::do_submit_drawdown(who, project_id, drawdown_id)
            },
            true => {
              // 1.Execute transactions if provided
              if let Some(mod_transactions) = transactions {
                // Ensure transactions are not empty
                ensure!(!mod_transactions.is_empty(), Error::<T>::EmptyTransactions);

                // Do execute transactions
                Self::do_execute_transactions(
                  who.clone(),
                  project_id,
                  drawdown_id,
                  mod_transactions,
                )?;

                // 2. Submit drawdown
                Self::do_submit_drawdown(who.clone(), project_id, drawdown_id)?;
              }

              // 3. Approve drawdown
              Self::do_approve_drawdown(who, project_id, drawdown_id)
            },
          }
        },
        None => {
          // Execute normal flow (EB5)
          Self::do_approve_drawdown(who, project_id, drawdown_id)
        },
      }
    }

    /// Reject a drawdown
    ///
    /// # Parameters:
    /// - origin: The administrator account who is rejecting the drawdown
    /// - project_id: The selected project id where the drawdown will be rejected
    /// - drawdown_id: The selected drawdown id to be rejected
    ///
    /// Then the next two feedback parameters are optional because depends on the drawdown type:
    /// #### EB5 drawdowns:
    /// - transactions_feedback: Administrator will provide feedback for each rejected
    /// transacion. This is a vector of tuples where each entry is composed by:
    /// * 0: The transaction id
    /// * 1: The transaction feedback
    ///
    /// #### Construction Loan & Developer Equity drawdowns:
    /// - drawdown_feedback: Administrator will provide feedback for the WHOLE drawdown.
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - All transactions associated to the drawdown will be rejected too. It's
    /// not possible to reject a drawdown without rejecting all of its transactions.
    /// (only for EB5 drawdowns).
    /// - For EB5 drawdowns, the administrator needs to provide feedback for
    /// each rejected transaction.
    /// - For Construction Loan & Developer Equity drawdowns, the administrator can provide
    /// feedback for the WHOLE drawdown.
    /// - After a builder re-submits a drawdown, the administrator will have to review
    /// the drawdown again.
    /// - After a builder re-submits a drawdown, the feedback field will be cleared
    ///   automatically.
    /// - If a single EB5 transaction is wrong, the administrator WILL reject the WHOLE
    ///   drawdown.
    /// There is no way to reject a single transaction.
    #[pallet::call_index(13)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn reject_drawdown(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      transactions_feedback: Option<TransactionsFeedback<T>>,
      drawdown_feedback: Option<FieldDescription>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_reject_drawdown(
        who,
        project_id,
        drawdown_id,
        transactions_feedback,
        drawdown_feedback,
      )
    }

    /// Bulk upload drawdowns.
    ///
    /// # Parameters:
    /// - origin: The administrator account who is uploading the drawdowns
    /// - project_id: The selected project id where the drawdowns will be uploaded
    /// - drawdown_id: The drawdowns to be uploaded
    /// - description: The description of the drawdown provided by the builder
    /// - total_amount: The total amount of the drawdown
    /// - documents: The documents provided by the builder for the drawdown
    ///
    /// # Considerations:
    /// - This function can only be called by a builder account
    /// - This extrinsic allows only one drawdown to be uploaded at the same time.
    /// - The drawdown will be automatically submitted.
    /// - Only available for Construction Loan & Developer Equity drawdowns.
    /// - After a builder uploads a drawdown, the administrator will have to review it.
    /// - After a builder re-submits a drawdown, the feedback field will be cleared
    ///   automatically.
    /// - Bulkuploads does not allow individual transactions.
    /// - After a builder uploads a drawdown, the administrator will have to
    /// insert each transaction manually.
    #[pallet::call_index(14)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn up_bulkupload(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      description: FieldDescription,
      total_amount: TotalAmount,
      documents: Documents<T>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be a builder

      Self::do_up_bulk_upload(who, project_id, drawdown_id, description, total_amount, documents)
    }

    /// Modifies the inflation rate of a project.
    ///
    /// # Parameters:
    /// - origin: The administrator account who is modifying the inflation rate
    /// - projects: The projects where the inflation rate will be modified.
    /// This is a vector of tuples where each entry is composed by:
    /// * 0: The project id
    /// * 1: The inflation rate
    /// * 2: The action to be performed (Create, Update or Delete)
    ///
    /// # Considerations:
    /// - This function can only be called by an administrator account
    /// - This extrinsic allows multiple projects to be modified at the same time.
    /// - The inflation rate can be created, updated or deleted.
    /// - The inflation rate is optional because depends on the CUDAction parameter:
    /// * **Create**: The inflation rate will be created. Project id, inflation rate and action
    ///   are required.
    /// * **Update**: The inflation rate will be updated. Project id, inflation rate and action
    ///   are required.
    /// * **Delete**: The inflation rate will be deleted. Project id and action are required.
    /// - The inflation rate can only be modified if the project is in the "started" status.
    #[pallet::call_index(15)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn inflation_rate(origin: OriginFor<T>, projects: ProjectsInflation<T>) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_execute_inflation_adjustment(who, projects)
    }

    // R E V E N U E S
    // --------------------------------------------------------------------------------------------

    /// This extrinsic is used to create, update or delete revenue transactions.
    /// It also allows that an array of revenue transactions
    /// to be saved as a draft or as submitted.
    ///
    /// # Parameters:
    /// */ - origin: The user account who is creating the revenue transactions
    /// - project_id: The selected project id where the revenue transactions will be created
    /// - revenue_id: The selected revenue id where the revenue transactions will be created
    /// - revenue_transactions: The revenue transactions to be created/updated/deleted.
    /// This entry is a vector of tuples where each entry is composed by:
    /// * 0: The job eligible id where the revenue transaction will be created
    /// * 1: The amount of the revenue transaction
    /// * 2: Documents associated to the revenue transaction
    /// * 3: The action to be performed on the revenue transaction (Create, Update or Delete)
    /// * 4: The revenue transaction id. This is required only if the action is being updated or
    ///   deleted.
    /// - submit: If true, the array of revenue transactions will be submitted to the
    ///   administrator.
    /// If false, the array of revenue transactions will be saved as a draft.
    ///
    /// # Considerations:
    /// - This function is only callable by a builder role account
    /// - This extrinsic allows multiple revenue transactions to be created/updated/deleted at
    ///   the same time.
    /// - The project id and revenue id are required for the reports.
    /// - revenue_transactions parameters are optional because depends on the action to be
    ///   performed:
    /// * **Create**: Job eligible id, Amount, Documents & action are required.
    /// * **Update**: Except for the revenue transaction id & action, all other parameters are
    ///   optional.
    /// * **Delete**: Only the revenue transaction id & action are required.
    /// - Multiple actions can be performed at the same time, but each must be performed on
    /// a different transaction. For example, you can create a new
    /// transaction and update another one at the same time.
    /// - Do not perform multiple actions over the same transaction in the same call, it could
    /// result in an unexpected behavior.
    /// - If a revenue is submitted, all transactions must be submitted too. If the revenue do
    ///   not contain
    /// any transaction, it will return an error.
    /// - After a revenue is submitted, it can not be updated or deleted.
    /// - After a revenue is rejected, builders will use again this extrinsic to update the
    /// transactions associated to a given revenue.
    #[pallet::call_index(16)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn submit_revenue(
      origin: OriginFor<T>,
      project_id: ProjectId,
      revenue_id: RevenueId,
      revenue_transactions: Option<RevenueTransactions<T>>,
      submit: bool,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      match submit {
        // Save revenue transactions as draft
        false => {
          // Do execute transactions
          Self::do_execute_revenue_transactions(
            who,
            project_id,
            revenue_id,
            revenue_transactions.ok_or(Error::<T>::RevenueTransactionsRequired)?,
          )
        },
        // Submit revenue transactions
        true => {
          // Check if there are transactions to execute
          if let Some(mod_revenue_transactions) = revenue_transactions {
            // Ensure transactions are not empty
            ensure!(!mod_revenue_transactions.is_empty(), Error::<T>::RevenueTransactionsEmpty);

            // Do execute transactions
            Self::do_execute_revenue_transactions(
              who.clone(),
              project_id,
              revenue_id,
              mod_revenue_transactions,
            )?;
          }

          // Do submit revenue
          Self::do_submit_revenue(who, project_id, revenue_id)
        },
      }
    }

    /// Approve a revenue
    ///
    /// # Parameters:
    /// - origin: The administrator account who is approving the revenue
    /// - project_id: The selected project id where the revenue will be approved
    /// - revenue_id: The selected revenue id to be approved
    ///
    /// # Considerations:
    /// - This function is only callable by an administrator role account
    /// - All transactions associated to the revenue will be approved too. It's
    /// not possible to approve a revenue without approving all of its transactions.
    /// - After a revenue is approved, it can not be updated or deleted.
    /// - After a revenue is approved, the next revenue will be created automatically.
    /// - After a revenue is rejected, administrators will use again this extrinsic to approve
    ///   the rejected revenue
    /// new revenue version uploaded by the builder.
    /// - The revenue status will be updated to Approved.
    #[pallet::call_index(17)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn approve_revenue(
      origin: OriginFor<T>,
      project_id: ProjectId,
      revenue_id: RevenueId,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_approve_revenue(who, project_id, revenue_id)
    }

    /// Reject a revenue
    ///
    /// # Parameters:
    /// - origin: The administrator account who is rejecting the revenue
    /// - project_id: The selected project id where the revenue will be rejected
    /// - revenue_id: The selected revenue id to be rejected
    /// - revenue_transactions_feedback: Administrator will provide feedback for each rejected
    /// transacion. This is a vector of tuples where each entry is composed by:
    /// * 0: The revenue transaction id
    /// * 1: The revenue transaction feedback
    ///
    /// # Considerations:
    /// - This function is only callable by an administrator role account
    /// - All transactions associated to the revenue will be rejected too. It's
    /// not possible to reject a revenue without rejecting all of its transactions.
    /// - Administrator needs to provide a feedback for each rejected transaction.
    /// - After a builder re-submits a revenue, the feedback field will be cleared
    ///   automatically.
    /// - If a single revenue transaction is wrong, the administrator WILL reject the WHOLE
    ///   revenue.
    /// There is no way to reject a single revenue transaction.
    #[pallet::call_index(18)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn reject_revenue(
      origin: OriginFor<T>,
      project_id: ProjectId,
      revenue_id: RevenueId,
      revenue_transactions_feedback: TransactionsFeedback<T>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_reject_revenue(who, project_id, revenue_id, revenue_transactions_feedback)
    }

    /// The following extrinsic is used to upload the bank confirming documents
    /// for a given drawdown.
    ///
    /// # Parameters:
    /// - origin: The administrator account who is uploading the confirming documents
    /// - project_id: The selected project id where the drawdown exists
    /// - drawdown_id: The selected drawdown id where the confirming documents will be uploaded
    /// - confirming_documents: The confirming documents to be uploaded. This field is optional
    /// because are required only when the action is Create or Update.
    /// - action: The action to be performed. It can be Create, Update or Delete
    /// 	* Create: project_id, drawdown_id and confirming_documents are required
    /// 	* Update: project_id, drawdown_id and confirming_documents are required
    /// 	* Delete: project_id and drawdown_id are required
    ///
    /// # Considerations:
    /// - This function is only callable by an administrator role account
    /// - The confirming documents are required only when the action is Create or Update.
    /// - The confirming documents are optional when the action is Delete.
    /// - After the confirming documents are uploaded, the drawdown status will be updated to
    /// "Confirmed". It will also update the status of all of its transactions to "Confirmed".
    /// - Update action will replace the existing confirming documents with the new ones.
    /// - Delete action will remove the existing confirming documents. It will also update the
    /// drawdown status to "Approved" and the status of all of its transactions to "Approved".
    /// It does a rollback of the drawdown.
    #[pallet::call_index(19)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn bank_confirming_documents(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      confirming_documents: Option<Documents<T>>,
      action: CUDAction,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_bank_confirming_documents(who, project_id, drawdown_id, confirming_documents, action)
    }

    /// The following extrinsic is used to cancel a drawdown submission.
    ///
    /// # Parameters:
    /// - origin: The builder account who is cancelling the drawdown submission
    /// - project_id: The selected project id where the drawdown exists
    /// - drawdown_id: The selected drawdown id to be cancelled
    ///
    /// # Considerations:
    /// - This function is only callable by a builder role account
    /// - The drawdown status will be rolled back to "Draft".
    /// - All of its transactions will be deleted.
    /// - The whole drawdown will be reset to its initial state, so be careful when using this
    #[pallet::call_index(20)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn reset_drawdown(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?;

      Self::do_reset_drawdown(who, project_id, drawdown_id)
    }

    /// Execute a recovery drawdown on a project. This function can only be called by an admin.
    ///
    /// Parameters:
    /// - `origin`: The administrator account who is executing the recovery drawdown
    /// - `project_id`: The ID of the project from which the recovery drawdown will be executed
    /// - `drawdown_id`: The ID of the drawdown from which the recovery drawdown will be executed
    /// - `transactions`: The list of transactions that will be executed in the recovery drawdown
    ///
    /// # Errors
    ///
    /// This function returns an error if:
    ///
    /// - The transaction origin is not a signed message from an admin account.
    /// - The project with the given ID does not exist.
    /// - The drawdown with the given ID does not exist.
    ///
    /// # Considerations:
    /// - This function is only callable by an administrator role account
    /// - The drawdown status won't be changed
    #[pallet::call_index(21)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn recovery_drawdown(
      origin: OriginFor<T>,
      project_id: ProjectId,
      drawdown_id: DrawdownId,
      transactions: Transactions<T>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_recovery_drawdown(who, project_id, drawdown_id, transactions)
    }
    /// Execute a recovery revenue on a project. This function can only be called by an admin.
    ///
    /// Parameters:
    /// - `origin`: The administrator account who is executing the recovery revenue
    /// - `project_id`: The ID of the project from which the recovery revenue will be executed
    /// - `revenue_id`: The ID of the revenue from which the recovery revenue will be executed
    /// - `transactions`: The list of transactions that will be executed in the recovery revenue
    ///
    /// # Errors
    ///
    /// This function returns an error if:
    ///
    /// - The transaction origin is not a signed message from an admin account.
    /// - The project with the given ID does not exist.
    /// - The revenue with the given ID does not exist.
    ///
    /// ### Considerations:
    /// - This function is only callable by an administrator role account
    /// - The revenue status won't be changed
    #[pallet::call_index(22)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn recovery_revenue(
      origin: OriginFor<T>,
      project_id: ProjectId,
      revenue_id: RevenueId,
      transactions: Transactions<T>,
    ) -> DispatchResult {
      let who = ensure_signed(origin)?; // origin need to be an admin

      Self::do_recovery_revenue(who, project_id, revenue_id, transactions)
    }

    /// Kill all the stored data.
    ///
    /// This function is used to kill ALL the stored data.
    /// Use it with caution!
    ///
    /// ### Parameters:
    /// - `origin`: The user who performs the action.
    ///
    /// ### Considerations:
    /// - This function is only available to the `admin` with sudo access.
    #[pallet::call_index(23)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn kill_storage(origin: OriginFor<T>) -> DispatchResult {
      T::RemoveOrigin::ensure_origin(origin.clone())?;
      let _ = <GlobalScope<T>>::kill();
      let _ = <UsersInfo<T>>::clear(1000, None);
      let _ = <ProjectsInfo<T>>::clear(1000, None);
      let _ = <UsersByProject<T>>::clear(1000, None);
      let _ = <ProjectsByUser<T>>::clear(1000, None);
      let _ = <ExpendituresInfo<T>>::clear(1000, None);
      let _ = <ExpendituresByProject<T>>::clear(1000, None);
      let _ = <DrawdownsInfo<T>>::clear(1000, None);
      let _ = <DrawdownsByProject<T>>::clear(1000, None);
      let _ = <TransactionsInfo<T>>::clear(1000, None);
      let _ = <TransactionsByDrawdown<T>>::clear(1000, None);
      let _ = <JobEligiblesInfo<T>>::clear(1000, None);
      let _ = <JobEligiblesByProject<T>>::clear(1000, None);
      let _ = <RevenuesInfo<T>>::clear(1000, None);
      let _ = <RevenuesByProject<T>>::clear(1000, None);
      let _ = <RevenueTransactionsInfo<T>>::clear(1000, None);
      let _ = <TransactionsByRevenue<T>>::clear(1000, None);

      T::Rbac::remove_pallet_storage(Self::pallet_id())?;
      Ok(())
    }

    #[pallet::call_index(24)]
    #[pallet::weight(Weight::from_ref_time(10_000) + T::DbWeight::get().writes(10))]
    pub fn set_new_admin_permissions(origin: OriginFor<T>) -> DispatchResult {
      T::RemoveOrigin::ensure_origin(origin.clone())?;
      // New permissions for fund admin administrator role
      let admin_id: [u8; 32] = ProxyRole::Administrator.id();
      let pallet_id = Self::pallet_id();

      let new_admin_permissions: Vec<Vec<u8>> = vec![
        ProxyPermission::RecoveryDrawdown.to_vec(),
        ProxyPermission::RecoveryRevenue.to_vec(),
        ProxyPermission::RecoveryTransaction.to_vec(),
        ProxyPermission::RecoveryRevenueTransaction.to_vec(),
        ProxyPermission::BulkUploadTransaction.to_vec(),
      ];

      T::Rbac::create_and_set_permissions(pallet_id.clone(), admin_id, new_admin_permissions)?;

      Ok(())
    }
  }
}