('email.skipSuffix'))
- if (sendToEmail && sendToEmail.endsWith('@undp.org')) {
+ if (sendToEmail && !sendToEmail.endsWith('@xeptagon.com')) {
return new Promise((resolve, reject) => {
this.transporter.sendMail({
from: this.sourceEmail,
to: sendToEmail,
- subject: template["subject"],
+ subject: this.getSubjectMessage(template["subject"], templateData),
text: this.getTemplateMessage(template["text"], templateData), // plain text body
html: this.getTemplateMessage(template["html"], templateData), // html body
}, function(error, info) {
diff --git a/lambda/services/src/shared/email/email.template.ts b/lambda/services/src/shared/email/email.template.ts
index f3cd71df6..e6d256739 100644
--- a/lambda/services/src/shared/email/email.template.ts
+++ b/lambda/services/src/shared/email/email.template.ts
@@ -1,16 +1,48 @@
export const EmailTemplates = {
- REGISTER_EMAIL: {
- subject: 'Welcome to Carbon Credit Registry',
+ ORGANISATION_CREATE:{
+ id: 'ORGANISATION_CREATE',
+ subject: 'Welcome!',
html: `
- Hi {{name}},
+ Welcome {{organisationName}},
+ Your Organisation has been registered with the {{countryName}} Carbon Registry as a {{organisationRole}} Organisation.
+ Explore the Registry here {{home}}.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ USER_CREATE: {
+ id: 'USER_CREATE',
+ subject: 'Welcome!',
+ html: `
+ Welcome {{name}},
+
+ Your account has been created for the {{countryName}} Carbon Credit
+ Registry. You can access your account using the temporary Homepage: {{home}}
+
+ User: {{email}}
+ Password (temporary): {{tempPassword}}
- Welcome to Carbon Credit Registry - {{countryName}}.
Your account has been created.
-
You can access using your temporary password: {{password}}
- {{apiKeyText}}
+ If you have any questions, feel free to email our customer success
+ team customer success team
+ (We’re lightning quick at replying.) We also offer live chat {{liveChat}}.
- Sincerely,
- The Carbon Credit Registry Team
- `,
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+
+
+ P.S.Need immediate help getting started? Check out our help
+ documentation {{helpDoc}}. Or, just reply to this email, the
+ {{countryName}} Carbon Credit Registry Team is always ready to help!
+
+
+
+ United Nations Development Programme
+ 1 United Nations Plaza
+ New York, NY USA 10001
+
+ `,
text: ''
},
API_KEY_EMAIL: {
@@ -50,5 +82,388 @@ export const EmailTemplates = {
The Carbon Credit Registry Team
`,
text: ''
+ },
+ CHANGE_PASSOWRD: {
+ id: 'CHANGE_PASSOWRD',
+ subject: 'Your password was changed',
+ html: `
+ Hi {{name}},
+ The password of your Carbon Registry account was changed successfully.
+ If you do not use {{countryName}} Carbon Credit Registry or did not request a password reset, please ignore this email or
+ contact support
+ if you have questions.
+
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `,
+ text: ''
+ },
+ PROGRAMME_CREATE: {
+ id: 'PROGRAMME_CREATE',
+ subject: 'New Programme Received for Authorisation',
+ html:`
+ Hi {{name}},
+
+ A new programme owned by {{organisationName}} is awaiting authorisation.
+
+ Click here to access all the programmes that require authorisation.
+
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_AUTHORISATION: {
+ id: 'PROGRAMME_AUTHORISATION',
+ subject: 'Programme authorised',
+ html:`
+ Hi {{name}},
+
+ {{programmeName}} of your organisation has been authorised on {{authorisedDate}} with the serial number {{serialNumber}}.
+
+
+ Click here for more details of the programme.
+
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_REJECTION: {
+ id: 'PROGRAMME_REJECTION',
+ subject: 'Programme Rejected',
+ html: `
+ Hi {{name}},
+
+ {{programmeName}} of your Organisation has been rejected on {{date}} due to the following reason/s:
+ {{reason}}
+
+ Click here {{pageLink}} for more details of the programme.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_ISSUANCE: {
+ id: 'CREDIT_ISSUANCE',
+ subject: 'Credits Issued',
+ html: `
+ Hi {{name}},
+
+ {{programmeName}} of your Organisation with the serial number {{serialNumber}} has been issued with {{credits}} credits.
+
+ Click here {{pageLink}} for more details of the programme.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_REQUISITIONS: {
+ id: 'CREDIT_TRANSFER_REQUISITIONS',
+ subject: 'Transfer Request Received',
+ html: `
+ Hi {{name}},
+
+ {{organisationName}} has requested to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}}.
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_CANCELLATION: {
+ id: 'CREDIT_TRANSFER_CANCELLATION',
+ subject: 'Transfer Request Cancelled',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} made by {{organisationName}} has been cancelled.
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_ACCEPTED: {
+ id: 'CREDIT_TRANSFER_ACCEPTED',
+ subject: 'Transfer Request Accepted',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} made by your organisation has been accepted by {{organisationName}}.
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_REJECTED: {
+ id: 'CREDIT_TRANSFER_REJECTED',
+ subject: 'Transfer Request Rejected',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}}
+ made by your organisation has been rejected by {{organisationName}}.
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team`
+ },
+ CREDIT_TRANSFER_GOV:{
+ id: 'CREDIT_TRANSFER_GOV',
+ subject: 'Transfer Request Received',
+ html: `
+ Hi {{name}},
+
+ {{government}} has requested your organisation to transfer {{credits}} credits with the serial number {{serialNumber}}
+ from {{programmeName}} to {{organisationName}}.
+
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_GOV_CANCELLATION: {
+ id: 'CREDIT_TRANSFER_GOV_CANCELLATION',
+ subject: 'Transfer Request Cancelled',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}}
+ to {{organisationName}} made by {{government}} has been cancelled.
+
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_GOV_ACCEPTED_TO_INITIATOR: {
+ id: 'CREDIT_TRANSFER_GOV_ACCEPTED_TO_INITIATOR',
+ subject: 'Transfer Request Accepted',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} made by your organisation has been accepted by {{organisationName}}.
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_GOV_ACCEPTED_TO_RECEIVER: {
+ id: 'CREDIT_TRANSFER_GOV_ACCEPTED_TO_RECEIVER',
+ subject: 'Credits Received',
+ html: `
+ Hi {{name}},
+
+ {{organisationName}} has transferred {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} to your organisation by accepting the request made by the {{government}}.
+ Click here {{pageLink}} for more details of the transfer request
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_TRANSFER_GOV_REJECTED: {
+ id: 'CREDIT_TRANSFER_GOV_REJECTED',
+ subject: 'Transfer Request Rejected',
+ html: `
+ Hi {{name}},
+
+ Request to transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} made by your organisation has been rejected by {{organisationName}}.
+ Click here {{pageLink}} for more details of the transfer request
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_SEND_DEVELOPER:{
+ id: 'CREDIT_SEND_DEVELOPER',
+ subject: 'Credits Received',
+ html: `
+ Hi {{name}},
+
+ {{organisationName}} has transferred {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} to your organisation.
+
+ Click here {{pageLink}} for more details of the transfer request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_CERTIFICATION:{
+ id: 'PROGRAMME_CERTIFICATION',
+ subject: 'Programme Certified by {{organisationName}}',
+ html: `
+ Hi {{name}},
+
+ The {{programmeName}} containing {{credits}} credits with the serial number {{serialNumber}} of your Organisation has been certified by {{organisationName}}.
+ Click here {{pageLink}} for more details of the certification.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_CERTIFICATION_REVOKE_BY_CERT:{
+ id: 'PROGRAMME_CERTIFICATION_REVOKE_BY_CERT',
+ subject: 'Programme Certificate Revoked by {{organisationName}}',
+ html: `
+ Hi {{name}},
+
+ The certification of the programme {{programmeName}} containing {{credits}} credits with the serial number {{serialNumber}} has been revoked by {{organisationName}}.
+ Click here {{pageLink}} for more details of the certification.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_PROGRAMME:{
+ id: 'PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_PROGRAMME',
+ subject: 'Programme Certificate Revoked by {{government}}',
+ html: `
+ Hi {{name}},
+
+ The certification given by {{organisationName}} for the programme {{programmeName}} containing {{credits}} credits with the serial number {{serialNumber}} has been revoked by the {{government}}.
+ Click here {{pageLink}} for more details of the certification.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_CERT:{
+ id: 'PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_CERT',
+ subject: 'Programme Certificate Revoked by {{government}}',
+ html: `
+ Hi {{name}},
+
+ The certification given by your organisation for the programme {{programmeName}} containing {{credits}} credits with the serial number {{serialNumber}} has been revoked by the {{government}}.
+ Click here {{pageLink}} for more details of the certification.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_CERTIFICATION_REVOKE_BY_SYSTEM:{
+ id: 'PROGRAMME_CERTIFICATION_REVOKE_BY_SYSTEM',
+ subject: 'Programme Certificate Revoked by the System',
+ html: `
+ Hi {{name}},
+
+ The certification given by {{organisationName}} for the programme {{programmeName}} containing {{credits}} credits with the serial number {{serialNumber}} has been revoked by the system as {{organisationName}} was deactivated.
+ Click here {{pageLink}} for more details of the certification.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ PROGRAMME_DEVELOPER_ORG_DEACTIVATION:{
+ id: 'PROGRAMME_DEVELOPER_ORG_DEACTIVATION',
+ subject: 'Organisation Deactivated',
+ html: `
+ Hi,
+
+ Your organisation has been deactivated by the {{government}}. Your organisation will still be visible but not other will be able to take place. Following are the effects of deactivation:
+ · All the users of the organisation were deactivated.
+ · All the credits owned by your organisation were frozen.
+ · All credit transfer requests sent and received by your organisation were cancelled.
+ · All the international transfer retire requests sent by your organisation were cancelled.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CERTIFIER_ORG_DEACTIVATION: {
+ id: 'CERTIFIER_ORG_DEACTIVATION',
+ subject: 'Organisation Deactivated',
+ html: `
+ Hi,
+
+ Your organisation has been deactivated by the {{government}}. Your organisation will still be visible but not other will be able to take place. Following are the effects of deactivation:
+ · All the users of the organisation were deactivated.
+ · All the certificates given by your organisation were revoked.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_RETIREMENT_BY_GOV:{
+ id: 'CREDIT_RETIREMENT_BY_GOV',
+ subject: 'Credits Retired',
+ html: `
+ Hi {{name}},
+
+ {{credits}} credits of the programme {{programmeName}} with the serial number {{serialNumber}} has been retired by the {{government}} as {{reason}}.
+ Click here {{pageLink}} for more details of the retirement.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_RETIREMENT_BY_DEV:{
+ id: 'CREDIT_RETIREMENT_BY_DEV',
+ subject: 'International Transfer Retire Request Received',
+ html: `
+ Hi {{name}},
+
+ {{organisationName}} has requested an international transfer retirement of {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}}.
+ Click here {{pageLink}} for more details of the international transfer retire request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_RETIREMENT_CANCEL:{
+ id: 'CREDIT_RETIREMENT_CANCEL',
+ subject: 'International Transfer Retire Request Cancelled',
+ html: `
+ Hi {{name}},
+
+ Request to internationally transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} to {{country}} made by {{organisationName}} has been cancelled.
+ Click here {{pageLink}} for more details of the international transfer retire request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_RETIREMENT_RECOGNITION:{
+ id: 'CREDIT_RETIREMENT_RECOGNITION',
+ subject: 'International Transfer Retire Request Recognised',
+ html: `
+ Hi {{name}},
+
+ Request to internationally transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} to {{country}} made by your organisation has been recognised.
+ Click here {{pageLink}} for more details of the international transfer retire request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ CREDIT_RETIREMENT_NOT_RECOGNITION:{
+ id: 'CREDIT_RETIREMENT_NOT_RECOGNITION',
+ subject: 'International Transfer Retire Request Not Recognised',
+ html: `
+ Hi {{name}},
+
+ Request to internationally transfer {{credits}} credits with the serial number {{serialNumber}} from {{programmeName}} to {{country}} made by your organisation has not been recognised.
+ Click here {{pageLink}} for more details of the international transfer retire request.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
+ },
+ ORG_REACTIVATION:{
+ id: 'ORG_REACTIVATION',
+ subject: 'Organisation Reactivated',
+ html: `
+ Hi
+
+ Your organisation has been reactivated by the {{government}}. Your organisation will be able to perform actions as before and all the users of the organisation will be reactivated.
+
+ Sincerely,
+ The {{countryName}} Carbon Credit Registry Team
+ `
}
};
diff --git a/lambda/services/src/shared/entities/company.entity.ts b/lambda/services/src/shared/entities/company.entity.ts
index 8f75f22ec..5a0fded3a 100644
--- a/lambda/services/src/shared/entities/company.entity.ts
+++ b/lambda/services/src/shared/entities/company.entity.ts
@@ -64,6 +64,9 @@ export class Company implements EntitySubject{
@Column("bigint", { nullable: true })
lastUpdateVersion: number;
+ @Column("bigint", { nullable: true })
+ creditTxTime: number;
+
@Column({nullable:true})
remarks: string;
diff --git a/lambda/services/src/shared/entities/programme.entity.ts b/lambda/services/src/shared/entities/programme.entity.ts
index e58c28abc..6e71829b7 100644
--- a/lambda/services/src/shared/entities/programme.entity.ts
+++ b/lambda/services/src/shared/entities/programme.entity.ts
@@ -73,14 +73,14 @@ export class Programme implements EntitySubject {
@Column({ type: "decimal", precision: 10, scale: PRECISION, nullable: true })
creditBalance: number;
- @Column({ type: "decimal", precision: 10, scale: PRECISION, nullable: true })
- creditRetired: number;
+ @Column("real", { array: true, nullable: true })
+ creditRetired: number[];
@Column("real", { array: true, nullable: true })
creditFrozen: number[];
- @Column({ type: "decimal", precision: 10, scale: PRECISION, nullable: true })
- creditTransferred: number;
+ @Column("real", { array: true, nullable: true })
+ creditTransferred: number[];
@Column({ nullable: true })
constantVersion: string;
@@ -132,6 +132,18 @@ export class Programme implements EntitySubject {
@Column({ type: "bigint" })
createdTime: number;
+ @Column({ type: "bigint", nullable: true })
+ authTime: number;
+
+ @Column({ type: "bigint", nullable: true })
+ creditUpdateTime: number;
+
+ @Column({ type: "bigint", nullable: true })
+ statusUpdateTime: number;
+
+ @Column({ type: "bigint", nullable: true })
+ certifiedTime: number;
+
@Column({ nullable: true })
txRef: string;
diff --git a/lambda/services/src/shared/entities/programme.transfer.ts b/lambda/services/src/shared/entities/programme.transfer.ts
index d1bb7b380..b9b969171 100644
--- a/lambda/services/src/shared/entities/programme.transfer.ts
+++ b/lambda/services/src/shared/entities/programme.transfer.ts
@@ -61,9 +61,15 @@ export class ProgrammeTransfer implements EntitySubject {
@Column({nullable: true})
comment: string;
+ @Column({nullable: true})
+ txRef: string;
+
@Column({type: "bigint"})
txTime: number;
-
+
+ @Column({type: "bigint", nullable: true})
+ createdTime: number;
+
@Column({
type: "enum",
enum: TransferStatus,
diff --git a/lambda/services/src/shared/entities/programmeTransfer.view.entity.ts b/lambda/services/src/shared/entities/programmeTransfer.view.entity.ts
index 39ea5ec40..8f27a3b83 100644
--- a/lambda/services/src/shared/entities/programmeTransfer.view.entity.ts
+++ b/lambda/services/src/shared/entities/programmeTransfer.view.entity.ts
@@ -5,7 +5,7 @@ import { ProgrammeTransfer } from "./programme.transfer";
@ViewEntity({
expression: `
SELECT programme_transfer.*, JSON_AGG(distinct "requester".*) as "requester", JSON_AGG(distinct "receiver".*) as "receiver",
- "prog"."creditBalance" as "creditBalance", "prog"."title" as "programmeTitle",
+ "prog"."creditBalance" as "creditBalance", "prog"."title" as "programmeTitle", "prog"."certifierId" as "programmeCertifierId",
"prog"."sector" as "programmeSector", JSON_AGG(distinct "certifier".*) as "certifier",
JSON_AGG(distinct "sender".*) as "sender", "prog"."proponentTaxVatId" as "proponentTaxVatId",
"prog"."proponentPercentage" as "proponentPercentage", "prog"."creditOwnerPercentage" as "creditOwnerPercentage",
@@ -26,6 +26,9 @@ export class ProgrammeTransferViewEntityQuery extends ProgrammeTransfer {
@ViewColumn()
programmeTitle: string;
+ @ViewColumn()
+ programmeCertifierId: number[]
+
@ViewColumn()
serialNo: string;
diff --git a/lambda/services/src/shared/enum/programme-status.enum.ts b/lambda/services/src/shared/enum/programme-status.enum.ts
index 8e235880f..64c843a25 100644
--- a/lambda/services/src/shared/enum/programme-status.enum.ts
+++ b/lambda/services/src/shared/enum/programme-status.enum.ts
@@ -5,4 +5,7 @@ export enum ProgrammeStage {
AWAITING_AUTHORIZATION = "AwaitingAuthorization",
AUTHORISED = "Authorised",
REJECTED = "Rejected",
+// ISSUED = "Issued",
+// RETIRED = "Retired",
+// TRANSFERRED = "Transferred",
}
diff --git a/lambda/services/src/shared/enum/stat.type.enum.ts b/lambda/services/src/shared/enum/stat.type.enum.ts
index b5e53111f..9af40b9ce 100644
--- a/lambda/services/src/shared/enum/stat.type.enum.ts
+++ b/lambda/services/src/shared/enum/stat.type.enum.ts
@@ -31,28 +31,32 @@ export enum StatType {
PROGRAMS_CERTIFIED = "PROGRAMS_CERTIFIED",
PROGRAMS_UNCERTIFIED = "PROGRAMS_UNCERTIFIED",
- AGG_PROGRAMME_BY_STATUS = 'AGG_PROGRAMME_BY_STATUS',
- AGG_PROGRAMME_BY_SECTOR = 'AGG_PROGRAMME_BY_SECTOR',
- MY_CREDIT = 'MY_CREDIT',
- PENDING_TRANSFER_INIT = 'PENDING_TRANSFER_INIT',
- PENDING_TRANSFER_RECV = 'PENDING_TRANSFER_RECV',
- CERTIFIED_BY_ME = 'CERTIFIED_BY_ME',
- REVOKED_BY_ME = 'REVOKED_BY_ME',
- CERTIFIED_REVOKED_BY_ME = 'CERTIFIED_REVOKED_BY_ME',
- ALL_AUTH_PROGRAMMES = 'ALL_AUTH_PROGRAMMES',
- ALL_AUTH_PROGRAMME_MINE = 'ALL_AUTH_PROGRAMME_MINE',
- CERTIFIED_PROGRAMMES = 'CERTIFIED_PROGRAMMES',
- REVOKED_PROGRAMMES = 'REVOKED_PROGRAMMES',
- CERTIFIED_REVOKED_PROGRAMMES = 'CERTIFIED_REVOKED_PROGRAMMES',
- CERTIFIED_BY_ME_BY_STATE = 'CERTIFIED_BY_ME_BY_STATE',
- CERTIFIED_BY_ME_BY_SECTOR = 'CERTIFIED_BY_ME_BY_SECTOR',
- MY_AGG_PROGRAMME_BY_STATUS = 'MY_AGG_PROGRAMME_BY_STATUS',
- MY_AGG_PROGRAMME_BY_SECTOR = 'MY_AGG_PROGRAMME_BY_SECTOR',
- MY_CERTIFIED_REVOKED_PROGRAMMES = 'MY_CERTIFIED_REVOKED_PROGRAMMES',
- ALL_PROGRAMME_LOCATION = 'ALL_PROGRAMME_LOCATION',
- MY_PROGRAMME_LOCATION = 'MY_PROGRAMME_LOCATION',
- MY_CERTIFIED_PROGRAMME_LOCATION = 'MY_CERTIFIED_PROGRAMME_LOCATION',
- ALL_TRANSFER_LOCATION = 'ALL_TRANSFER_LOCATION',
- MY_TRANSFER_LOCATION = 'MY_TRANSFER_LOCATION',
- MY_CERTIFIED_TRANSFER_LOCATION = 'MY_CERTIFIED_TRANSFER_LOCATION',
+ AGG_PROGRAMME_BY_STATUS = "AGG_PROGRAMME_BY_STATUS",
+ AGG_PROGRAMME_BY_SECTOR = "AGG_PROGRAMME_BY_SECTOR",
+ MY_CREDIT = "MY_CREDIT",
+ PENDING_TRANSFER_INIT = "PENDING_TRANSFER_INIT",
+ PENDING_TRANSFER_RECV = "PENDING_TRANSFER_RECV",
+ CERTIFIED_BY_ME = "CERTIFIED_BY_ME",
+ REVOKED_BY_ME = "REVOKED_BY_ME",
+ CERTIFIED_REVOKED_BY_ME = "CERTIFIED_REVOKED_BY_ME",
+ UNCERTIFIED_BY_ME = "UNCERTIFIED_BY_ME",
+ ALL_AUTH_PROGRAMMES = "ALL_AUTH_PROGRAMMES",
+ ALL_AUTH_PROGRAMME_MINE = "ALL_AUTH_PROGRAMME_MINE",
+ CERTIFIED_PROGRAMMES = "CERTIFIED_PROGRAMMES",
+ REVOKED_PROGRAMMES = "REVOKED_PROGRAMMES",
+ CERTIFIED_REVOKED_PROGRAMMES = "CERTIFIED_REVOKED_PROGRAMMES",
+ CERTIFIED_BY_ME_BY_STATE = "CERTIFIED_BY_ME_BY_STATE",
+ CERTIFIED_BY_ME_BY_SECTOR = "CERTIFIED_BY_ME_BY_SECTOR",
+ MY_AGG_PROGRAMME_BY_STATUS = "MY_AGG_PROGRAMME_BY_STATUS",
+ MY_AGG_PROGRAMME_BY_SECTOR = "MY_AGG_PROGRAMME_BY_SECTOR",
+ MY_CERTIFIED_REVOKED_PROGRAMMES = "MY_CERTIFIED_REVOKED_PROGRAMMES",
+ ALL_PROGRAMME_LOCATION = "ALL_PROGRAMME_LOCATION",
+ MY_PROGRAMME_LOCATION = "MY_PROGRAMME_LOCATION",
+ // MY_CERTIFIED_PROGRAMME_LOCATION = 'MY_CERTIFIED_PROGRAMME_LOCATION',
+ ALL_TRANSFER_LOCATION = "ALL_TRANSFER_LOCATION",
+ MY_TRANSFER_LOCATION = "MY_TRANSFER_LOCATION",
+ MY_CERTIFIED_TRANSFER_LOCATION = "MY_CERTIFIED_TRANSFER_LOCATION",
+ AGG_AUTH_PROGRAMME_BY_STATUS = "AGG_AUTH_PROGRAMME_BY_STATUS",
+ MY_AGG_AUTH_PROGRAMME_BY_STATUS = "MY_AGG_AUTH_PROGRAMME_BY_STATUS",
+ AUTH_CERTIFIED_BY_ME_BY_STATE = "AUTH_CERTIFIED_BY_ME_BY_STATE",
}
diff --git a/lambda/services/src/shared/enum/system.action.type.ts b/lambda/services/src/shared/enum/system.action.type.ts
new file mode 100644
index 000000000..0d44f41bc
--- /dev/null
+++ b/lambda/services/src/shared/enum/system.action.type.ts
@@ -0,0 +1,4 @@
+export enum SystemActionType {
+ SUSPEND_AUTO_CANCEL = 'SUSPEND_AUTO_CANCEL',
+ SUSPEND_REVOKE = 'SUSPEND_REVOKE'
+}
\ No newline at end of file
diff --git a/lambda/services/src/shared/enum/txtype.enum.ts b/lambda/services/src/shared/enum/txtype.enum.ts
index 7fddeda67..66cd0b934 100644
--- a/lambda/services/src/shared/enum/txtype.enum.ts
+++ b/lambda/services/src/shared/enum/txtype.enum.ts
@@ -8,4 +8,5 @@ export enum TxType {
REVOKE = '6',
FREEZE = '7',
AUTH = '8',
+ UNFREEZE = '9',
}
\ No newline at end of file
diff --git a/lambda/services/src/shared/programme-ledger/programme-ledger.service.ts b/lambda/services/src/shared/programme-ledger/programme-ledger.service.ts
index 4b8b8b70c..1f288eee3 100644
--- a/lambda/services/src/shared/programme-ledger/programme-ledger.service.ts
+++ b/lambda/services/src/shared/programme-ledger/programme-ledger.service.ts
@@ -272,21 +272,33 @@ export class ProgrammeLedgerService {
programme.txTime = new Date().getTime();
programme.txRef = `${name}#${transfer.requestId}#${transfer.retirementType}#${reason}`;
+ const compIndex = programme.companyId.indexOf(transfer.fromCompanyId);
+ if (compIndex < 0) {
+ throw new HttpException(
+ `Company ${transfer.fromCompanyId} does not own the programme`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
if (isRetirement) {
// if (programme.creditBalance == transfer.creditAmount) {
// programme.currentStage = ProgrammeStage.RETIRED;
// }
programme.txType = TxType.RETIRE;
if (!programme.creditRetired) {
- programme.creditRetired = 0;
+ programme.creditRetired = new Array(
+ programme.creditOwnerPercentage.length
+ ).fill(0);
}
- programme.creditRetired += transfer.creditAmount;
+ programme.creditRetired[compIndex] += transfer.creditAmount;
+
} else {
programme.txType = TxType.TRANSFER;
if (!programme.creditTransferred) {
- programme.creditTransferred = 0;
+ programme.creditTransferred = new Array(
+ programme.creditOwnerPercentage.length
+ ).fill(0);
}
- programme.creditTransferred += transfer.creditAmount;
+ programme.creditTransferred[compIndex] += transfer.creditAmount;
}
programme.creditChange = transfer.creditAmount;
programme.creditBalance -= transfer.creditAmount;
@@ -504,11 +516,11 @@ export class ProgrammeLedgerService {
public async revokeCompanyCertifications(
companyId: number,
- reason: string,
- user: string
+ user: string,
+ sendRevokeEmail: Function
): Promise {
this.logger.log(
- `Freezing programme credits reason:${reason} companyId:${companyId} user:${user}`
+ `Freezing programme credits companyId:${companyId} user:${user}`
);
const getQueries = {};
companyId = Number(companyId);
@@ -540,7 +552,7 @@ export class ProgrammeLedgerService {
const prvTxTime = programme.txTime;
programme.txTime = new Date().getTime();
- programme.txRef = `${user}#${reason}`;
+ programme.txRef = `${user}`;
programme.txType = TxType.REVOKE;
programme.certifierId.splice(index, 1);
@@ -556,6 +568,8 @@ export class ProgrammeLedgerService {
};
programmesId.push(programme.programmeId);
+
+ sendRevokeEmail(programme);
}
// updatedProgramme = programme;
return [updateMap, updateWhere, {}];
@@ -567,12 +581,11 @@ export class ProgrammeLedgerService {
public async freezeCompany(
companyId: number,
- reason: string,
user: any,
- companyName: string
+ isFreeze: boolean
): Promise {
this.logger.log(
- `Freezing programme credits reason:${reason} companyId:${companyId} user:${user.id}`
+ `Freezing programme credits companyId:${companyId} user:${user}`
);
const getQueries = {};
companyId = Number(companyId);
@@ -605,31 +618,43 @@ export class ProgrammeLedgerService {
);
}
- if (programme.companyId.length > 1) {
- if (!programme.creditOwnerPercentage) {
- throw new HttpException(
- "Not ownership percentage for the company",
- HttpStatus.BAD_REQUEST
- );
+ if(isFreeze){
+ if (programme.companyId.length > 1) {
+ if (!programme.creditOwnerPercentage) {
+ throw new HttpException(
+ "Not ownership percentage for the company",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ } else {
+ programme.creditOwnerPercentage = [100];
}
- } else {
- programme.creditOwnerPercentage = [100];
- }
- const freezeCredit =
- (programme.creditBalance * programme.creditOwnerPercentage[index]) /
- 100;
- if (!programme.creditFrozen) {
- programme.creditFrozen = new Array(
- programme.creditOwnerPercentage.length
- ).fill(0);
+ const freezeCredit =this.round2Precision(
+ (programme.creditBalance * programme.creditOwnerPercentage[index]) /
+ 100);
+ if (!programme.creditFrozen) {
+ programme.creditFrozen = new Array(
+ programme.creditOwnerPercentage.length
+ ).fill(0);
+ }
+ if(freezeCredit === 0)
+ continue;
+ programme.creditFrozen[index] = freezeCredit;
+ }else{
+ if(programme.creditFrozen === undefined || programme.creditFrozen[index] === null)
+ continue;
+ const unFrozenCredit = this.round2Precision(programme.creditFrozen[index]);
+ if(unFrozenCredit === 0)
+ continue;
+ programme.creditChange = unFrozenCredit;
+ programme.creditFrozen[index] = 0;
}
const prvTxTime = programme.txTime;
(programme.txTime = new Date().getTime()),
- (programme.txRef = `${user.companyId}#${user.companyName}#${user.id}#${user.name}#${companyName}`),
- (programme.txType = TxType.FREEZE);
- programme.creditFrozen[index] = freezeCredit;
+ (programme.txRef = user),
+ (programme.txType = isFreeze ? TxType.FREEZE : TxType.UNFREEZE);
updateMap[this.ledger.tableName + "#" + programme.programmeId] = {
currentStage: programme.currentStage,
@@ -842,8 +867,12 @@ export class ProgrammeLedgerService {
}
private round2Precision(val) {
- return parseFloat(val.toFixed(PRECISION));
+ if(val)
+ return parseFloat(val.toFixed(PRECISION));
+ else
+ return 0;
}
+
public async authProgrammeStatus(
programmeId: string,
countryCodeA2: string,
diff --git a/lambda/services/src/shared/programme/programme.module.ts b/lambda/services/src/shared/programme/programme.module.ts
index 4c90927bd..fd76b7b0a 100644
--- a/lambda/services/src/shared/programme/programme.module.ts
+++ b/lambda/services/src/shared/programme/programme.module.ts
@@ -13,6 +13,7 @@ import { Company } from '../entities/company.entity';
import { ProgrammeQueryEntity } from '../entities/programme.view.entity';
import { ProgrammeTransferViewEntityQuery } from '../entities/programmeTransfer.view.entity';
import { UserModule } from '../user/user.module';
+import { EmailHelperModule } from '../email-helper/email-helper.module';
@Module({
imports: [
@@ -22,7 +23,8 @@ import { UserModule } from '../user/user.module';
UtilModule,
CompanyModule,
EmailModule,
- UserModule
+ UserModule,
+ EmailHelperModule
],
providers: [Logger, ProgrammeService],
exports: [ProgrammeService]
diff --git a/lambda/services/src/shared/programme/programme.service.ts b/lambda/services/src/shared/programme/programme.service.ts
index 15d6f634b..97082945a 100644
--- a/lambda/services/src/shared/programme/programme.service.ts
+++ b/lambda/services/src/shared/programme/programme.service.ts
@@ -1,813 +1,1690 @@
-import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
-import { ProgrammeDto } from '../dto/programme.dto';
-import { Programme } from '../entities/programme.entity';
-import { ProgrammeLedgerService } from '../programme-ledger/programme-ledger.service';
-import { instanceToPlain, plainToClass } from 'class-transformer';
-import { ProgrammeStage } from '../enum/programme-status.enum';
-import { AgricultureConstants, AgricultureCreationRequest, calculateCredit, SolarConstants, SolarCreationRequest } from 'carbon-credit-calculator';
-import { QueryDto } from '../dto/query.dto';
-import { InjectRepository } from '@nestjs/typeorm';
-import { In, Repository } from 'typeorm';
-import { PrimaryGeneratedColumnType } from 'typeorm/driver/types/ColumnTypes';
-import { CounterService } from '../util/counter.service';
-import { CounterType } from '../util/counter.type.enum';
-import { ConstantEntity } from '../entities/constants.entity';
-import { DataResponseDto } from '../dto/data.response.dto';
-import { ConstantUpdateDto } from '../dto/constants.update.dto';
-import { ProgrammeApprove } from '../dto/programme.approve';
-import { DataListResponseDto } from '../dto/data.list.response';
-import { BasicResponseDto } from '../dto/basic.response.dto';
-import { ConfigService } from '@nestjs/config';
-import { TypeOfMitigation } from '../enum/typeofmitigation.enum';
-import { CompanyService } from '../company/company.service';
-import { ProgrammeTransferRequest } from '../dto/programme.transfer.request';
-import { EmailService } from '../email/email.service';
-import { EmailTemplates } from '../email/email.template';
-import { User } from '../entities/user.entity';
-import { ProgrammeTransfer } from '../entities/programme.transfer';
-import { TransferStatus } from '../enum/transform.status.enum';
-import { ProgrammeTransferApprove } from '../dto/programme.transfer.approve';
-import { ProgrammeTransferReject } from '../dto/programme.transfer.reject';
-import { Company } from '../entities/company.entity';
-import { HelperService } from '../util/helpers.service';
-import { CompanyRole } from '../enum/company.role.enum';
-import { ProgrammeCertify } from '../dto/programme.certify';
-import { ProgrammeQueryEntity } from '../entities/programme.view.entity';
-import { ProgrammeTransferViewEntityQuery } from '../entities/programmeTransfer.view.entity';
-import { ProgrammeRetire } from '../dto/programme.retire';
-import { ProgrammeTransferCancel } from '../dto/programme.transfer.cancel';
-import { CompanyState } from '../enum/company.state.enum';
-import { ProgrammeReject } from '../dto/programme.reject';
-import { ProgrammeIssue } from '../dto/programme.issue';
-import { RetireType } from '../enum/retire.type.enum';
-import { UserService } from '../user/user.service';
-import { Role } from '../casl/role.enum';
-
-export declare function PrimaryGeneratedColumn(options: PrimaryGeneratedColumnType): Function;
+import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
+import { ProgrammeDto } from "../dto/programme.dto";
+import { Programme } from "../entities/programme.entity";
+import { ProgrammeLedgerService } from "../programme-ledger/programme-ledger.service";
+import { instanceToPlain, plainToClass } from "class-transformer";
+import { ProgrammeStage } from "../enum/programme-status.enum";
+import {
+ AgricultureConstants,
+ AgricultureCreationRequest,
+ calculateCredit,
+ SolarConstants,
+ SolarCreationRequest,
+} from "carbon-credit-calculator";
+import { QueryDto } from "../dto/query.dto";
+import { InjectRepository } from "@nestjs/typeorm";
+import { In, Repository } from "typeorm";
+import { PrimaryGeneratedColumnType } from "typeorm/driver/types/ColumnTypes";
+import { CounterService } from "../util/counter.service";
+import { CounterType } from "../util/counter.type.enum";
+import { ConstantEntity } from "../entities/constants.entity";
+import { DataResponseDto } from "../dto/data.response.dto";
+import { ConstantUpdateDto } from "../dto/constants.update.dto";
+import { ProgrammeApprove } from "../dto/programme.approve";
+import { DataListResponseDto } from "../dto/data.list.response";
+import { BasicResponseDto } from "../dto/basic.response.dto";
+import { ConfigService } from "@nestjs/config";
+import { TypeOfMitigation } from "../enum/typeofmitigation.enum";
+import { CompanyService } from "../company/company.service";
+import { ProgrammeTransferRequest } from "../dto/programme.transfer.request";
+import { EmailService } from "../email/email.service";
+import { EmailTemplates } from "../email/email.template";
+import { User } from "../entities/user.entity";
+import { ProgrammeTransfer } from "../entities/programme.transfer";
+import { TransferStatus } from "../enum/transform.status.enum";
+import { ProgrammeTransferApprove } from "../dto/programme.transfer.approve";
+import { ProgrammeTransferReject } from "../dto/programme.transfer.reject";
+import { Company } from "../entities/company.entity";
+import { HelperService } from "../util/helpers.service";
+import { CompanyRole } from "../enum/company.role.enum";
+import { ProgrammeCertify } from "../dto/programme.certify";
+import { ProgrammeQueryEntity } from "../entities/programme.view.entity";
+import { ProgrammeTransferViewEntityQuery } from "../entities/programmeTransfer.view.entity";
+import { ProgrammeRetire } from "../dto/programme.retire";
+import { ProgrammeTransferCancel } from "../dto/programme.transfer.cancel";
+import { CompanyState } from "../enum/company.state.enum";
+import { ProgrammeReject } from "../dto/programme.reject";
+import { ProgrammeIssue } from "../dto/programme.issue";
+import { RetireType } from "../enum/retire.type.enum";
+import { EmailHelperService } from "../email-helper/email-helper.service";
+import { UserService } from "../user/user.service";
+import { use } from "passport";
+import { SystemActionType } from "../enum/system.action.type";
+import { CountryService } from "../util/country.service";
+
+export declare function PrimaryGeneratedColumn(
+ options: PrimaryGeneratedColumnType
+): Function;
@Injectable()
export class ProgrammeService {
-
- constructor(
- private programmeLedger: ProgrammeLedgerService,
- private counterService: CounterService,
- private configService: ConfigService,
- private companyService: CompanyService,
- private userService: UserService,
- private emailService: EmailService,
- private helperService: HelperService,
- @InjectRepository(Programme) private programmeRepo: Repository,
- @InjectRepository(ProgrammeQueryEntity) private programmeViewRepo: Repository,
- @InjectRepository(ProgrammeTransferViewEntityQuery) private programmeTransferViewRepo: Repository,
- @InjectRepository(Company) private companyRepo: Repository,
- @InjectRepository(ProgrammeTransfer) private programmeTransferRepo: Repository,
- @InjectRepository(ConstantEntity) private constantRepo: Repository,
- private logger: Logger) { }
-
- private toProgramme(programmeDto: ProgrammeDto): Programme {
- const data = instanceToPlain(programmeDto);
- this.logger.verbose('Converted programme', JSON.stringify(data))
- return plainToClass(Programme, data);
- }
-
- private async getCreditRequest(programmeDto: ProgrammeDto, constants: ConstantEntity) {
- switch (programmeDto.typeOfMitigation) {
- case TypeOfMitigation.AGRICULTURE:
- const ar = new AgricultureCreationRequest()
- ar.duration = (programmeDto.endTime - programmeDto.startTime)
- ar.durationUnit = "s"
- ar.landArea = programmeDto.agricultureProperties.landArea;
- ar.landAreaUnit = programmeDto.agricultureProperties.landAreaUnit
- if (constants) {
- ar.agricultureConstants = constants.data as AgricultureConstants
- }
- return ar;
- case TypeOfMitigation.SOLAR:
- const sr = new SolarCreationRequest()
- sr.buildingType = programmeDto.solarProperties.consumerGroup;
- sr.energyGeneration = programmeDto.solarProperties.energyGeneration;
- sr.energyGenerationUnit = programmeDto.solarProperties.energyGenerationUnit
- if (constants) {
- sr.solarConstants = constants.data as SolarConstants
- }
- return sr;
- }
- throw Error("Not implemented for mitigation type " + programmeDto.typeOfMitigation)
+ private userNameCache: any = {};
+
+ constructor(
+ private programmeLedger: ProgrammeLedgerService,
+ private counterService: CounterService,
+ private configService: ConfigService,
+ private companyService: CompanyService,
+ private userService: UserService,
+ private emailService: EmailService,
+ private helperService: HelperService,
+ private emailHelperService: EmailHelperService,
+ private readonly countryService: CountryService,
+ @InjectRepository(Programme) private programmeRepo: Repository,
+ @InjectRepository(ProgrammeQueryEntity)
+ private programmeViewRepo: Repository,
+ @InjectRepository(ProgrammeTransferViewEntityQuery)
+ private programmeTransferViewRepo: Repository,
+ @InjectRepository(Company) private companyRepo: Repository,
+ @InjectRepository(ProgrammeTransfer)
+ private programmeTransferRepo: Repository,
+ @InjectRepository(ConstantEntity)
+ private constantRepo: Repository,
+ private logger: Logger
+ ) {}
+
+ private toProgramme(programmeDto: ProgrammeDto): Programme {
+ const data = instanceToPlain(programmeDto);
+ this.logger.verbose("Converted programme", JSON.stringify(data));
+ return plainToClass(Programme, data);
+ }
+
+ private async getCreditRequest(
+ programmeDto: ProgrammeDto,
+ constants: ConstantEntity
+ ) {
+ switch (programmeDto.typeOfMitigation) {
+ case TypeOfMitigation.AGRICULTURE:
+ const ar = new AgricultureCreationRequest();
+ ar.duration = programmeDto.endTime - programmeDto.startTime;
+ ar.durationUnit = "s";
+ ar.landArea = programmeDto.agricultureProperties.landArea;
+ ar.landAreaUnit = programmeDto.agricultureProperties.landAreaUnit;
+ if (constants) {
+ ar.agricultureConstants = constants.data as AgricultureConstants;
+ }
+ return ar;
+ case TypeOfMitigation.SOLAR:
+ const sr = new SolarCreationRequest();
+ sr.buildingType = programmeDto.solarProperties.consumerGroup;
+ sr.energyGeneration = programmeDto.solarProperties.energyGeneration;
+ sr.energyGenerationUnit =
+ programmeDto.solarProperties.energyGenerationUnit;
+ if (constants) {
+ sr.solarConstants = constants.data as SolarConstants;
+ }
+ return sr;
+ }
+ throw Error(
+ "Not implemented for mitigation type " + programmeDto.typeOfMitigation
+ );
+ }
+
+ async transferReject(req: ProgrammeTransferReject, approver: User) {
+ this.logger.log(
+ `Programme reject ${JSON.stringify(req)} ${approver.companyId}`
+ );
+
+ const pTransfer = await this.programmeTransferRepo.findOneBy({
+ requestId: req.requestId,
+ });
+
+ if (!pTransfer) {
+ throw new HttpException(
+ "Transfer request does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
- async transferReject(req: ProgrammeTransferReject, approverCompanyId: number) {
-
- this.logger.log(`Programme reject ${JSON.stringify(req)} ${approverCompanyId}`);
+ if (pTransfer.status == TransferStatus.CANCELLED) {
+ throw new HttpException(
+ "Transfer request already cancelled",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const pTransfer = await this.programmeTransferRepo.findOneBy({
- requestId: req.requestId,
- })
+ if (
+ !pTransfer.isRetirement &&
+ pTransfer.fromCompanyId != approver.companyId
+ ) {
+ throw new HttpException(
+ "Invalid approver for the transfer request",
+ HttpStatus.FORBIDDEN
+ );
+ }
+ if (pTransfer.isRetirement && pTransfer.toCompanyId != approver.companyId) {
+ throw new HttpException(
+ "Invalid approver for the retirement request",
+ HttpStatus.FORBIDDEN
+ );
+ }
- if (!pTransfer) {
- throw new HttpException("Transfer request does not exist", HttpStatus.BAD_REQUEST)
- }
+ const result = await this.programmeTransferRepo
+ .update(
+ {
+ requestId: req.requestId,
+ status: TransferStatus.PENDING,
+ },
+ {
+ status: pTransfer.isRetirement
+ ? TransferStatus.NOTRECOGNISED
+ : TransferStatus.REJECTED,
+ txTime: new Date().getTime(),
+ txRef: `${req.comment}#${approver.companyId}#${approver.id}`,
+ }
+ )
+ .catch((err) => {
+ this.logger.error(err);
+ return err;
+ });
+
+ const initiatorCompanyDetails = await this.companyService.findByCompanyId(
+ pTransfer.initiatorCompanyId
+ );
+
+ if (result.affected > 0) {
+ if (pTransfer.isRetirement) {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ pTransfer.fromCompanyId,
+ EmailTemplates.CREDIT_RETIREMENT_NOT_RECOGNITION,
+ {
+ credits: pTransfer.creditAmount,
+ country: pTransfer.toCompanyMeta.country,
+ },
+ 0,
+ pTransfer.programmeId
+ );
+ } else if (
+ initiatorCompanyDetails.companyRole === CompanyRole.GOVERNMENT
+ ) {
+ await this.emailHelperService.sendEmailToGovernmentAdmins(
+ EmailTemplates.CREDIT_TRANSFER_GOV_REJECTED,
+ { credits: pTransfer.creditAmount },
+ pTransfer.programmeId,
+ pTransfer.fromCompanyId
+ );
+ } else {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ pTransfer.initiatorCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_REJECTED,
+ { credits: pTransfer.creditAmount },
+ pTransfer.fromCompanyId,
+ pTransfer.programmeId
+ );
+ }
+ return new BasicResponseDto(HttpStatus.OK, "Successfully rejected");
+ }
- if (pTransfer.status == TransferStatus.CANCELLED) {
- throw new HttpException("Transfer request already cancelled", HttpStatus.BAD_REQUEST)
+ throw new HttpException(
+ "No pending transfer request found",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ async getTransferByProgrammeId(
+ programmeId: string,
+ abilityCondition: string,
+ user: User
+ ): Promise {
+ const query: QueryDto = {
+ page: 1,
+ size: 30,
+ filterAnd: [
+ {
+ key: "programmeId",
+ operation: "=",
+ value: String(programmeId),
+ },
+ ],
+ filterOr: undefined,
+ sort: undefined,
+ };
+
+ const resp = await this.programmeTransferViewRepo
+ .createQueryBuilder("programme_transfer")
+ .where(
+ this.helperService.generateWhereSQL(
+ query,
+ this.helperService.parseMongoQueryToSQLWithTable(
+ "programme_transfer",
+ abilityCondition
+ )
+ )
+ )
+ .orderBy(
+ query?.sort?.key &&
+ this.helperService.generateSortCol(query?.sort?.key),
+ query?.sort?.order
+ )
+ .offset(query.size * query.page - query.size)
+ .limit(query.size)
+ .getManyAndCount();
+
+ if (resp && resp.length > 0) {
+ for (const e of resp[0]) {
+ console.log(e);
+ e.certifier =
+ e.certifier.length > 0 && e.certifier[0] === null ? [] : e.certifier;
+ if (
+ e.isRetirement &&
+ e.retirementType == RetireType.CROSS_BORDER &&
+ e.toCompanyMeta.country
+ ) {
+ e.toCompanyMeta["countryName"] =
+ await this.countryService.getCountryName(e.toCompanyMeta.country);
+ }
+
+ let usrId = undefined;
+ let userCompany = undefined;
+ if (e["txRef"] != undefined && e["txRef"] != null) {
+ const parts = e["txRef"]?.split("#");
+ if (parts.length > 2) {
+ usrId = parts[2];
+ userCompany = parts[1];
+ }
+ } else {
+ usrId = e["initiator"];
+ userCompany = e["initiatorCompanyId"];
}
- if (!pTransfer.isRetirement && pTransfer.fromCompanyId != approverCompanyId) {
- throw new HttpException("Invalid approver for the transfer request", HttpStatus.FORBIDDEN)
+ if (
+ user.companyRole === CompanyRole.GOVERNMENT ||
+ Number(userCompany) === Number(user.companyId)
+ ) {
+ e["userName"] = await this.getUserName(usrId);
}
- if (pTransfer.isRetirement && pTransfer.toCompanyId != approverCompanyId) {
- throw new HttpException("Invalid approver for the retirement request", HttpStatus.FORBIDDEN)
+ }
+ }
+ return new DataListResponseDto(
+ resp.length > 0 ? resp[0] : undefined,
+ resp.length > 1 ? resp[1] : undefined
+ );
+ }
+
+ async queryProgrammeTransfers(
+ query: QueryDto,
+ abilityCondition: string,
+ user: User
+ ): Promise {
+ const resp = await this.programmeTransferViewRepo
+ .createQueryBuilder("programme_transfer")
+ .where(
+ this.helperService.generateWhereSQL(
+ query,
+ this.helperService.parseMongoQueryToSQLWithTable(
+ "programme_transfer",
+ abilityCondition
+ )
+ )
+ )
+ .orderBy(
+ query?.sort?.key &&
+ this.helperService.generateSortCol(query?.sort?.key),
+ query?.sort?.order,
+ query?.sort?.nullFirst !== undefined
+ ? query?.sort?.nullFirst === true
+ ? "NULLS FIRST"
+ : "NULLS LAST"
+ : undefined
+ )
+ .offset(query.size * query.page - query.size)
+ .limit(query.size)
+ .getManyAndCount();
+
+ if (resp && resp.length > 0) {
+ for (const e of resp[0]) {
+ e.certifier =
+ e.certifier.length > 0 && e.certifier[0] === null ? [] : e.certifier;
+
+ if (e.toCompanyMeta && e.toCompanyMeta.country) {
+ e.toCompanyMeta['countryName'] = await this.countryService.getCountryName(e.toCompanyMeta.country)
}
+ }
+ }
+ return new DataListResponseDto(
+ resp.length > 0 ? resp[0] : undefined,
+ resp.length > 1 ? resp[1] : undefined
+ );
+ }
+
+ async transferApprove(req: ProgrammeTransferApprove, approver: User) {
+ // TODO: Handle transaction, can happen
+ console.log("Approver", approver);
+ const transfer = await this.programmeTransferRepo.findOneBy({
+ requestId: req.requestId,
+ });
+
+ if (!transfer) {
+ throw new HttpException(
+ "Transfer request does not exist",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const result = await this.programmeTransferRepo.update({
- requestId: req.requestId,
- status: TransferStatus.PENDING
- }, {
- status: pTransfer.isRetirement ? TransferStatus.NOTRECOGNISED : TransferStatus.REJECTED
- }).catch((err) => {
- this.logger.error(err);
- return err;
- });
+ if (transfer.status == TransferStatus.CANCELLED) {
+ throw new HttpException(
+ "Transfer request already cancelled",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (result.affected > 0) {
- return new BasicResponseDto(HttpStatus.OK, "Successfully rejected");
- }
+ if (
+ transfer.status == TransferStatus.APPROVED ||
+ transfer.status == TransferStatus.RECOGNISED
+ ) {
+ throw new HttpException(
+ "Transfer already approved",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- throw new HttpException("No pending transfer request found", HttpStatus.BAD_REQUEST)
+ if (
+ !transfer.isRetirement &&
+ transfer.fromCompanyId != approver.companyId
+ ) {
+ throw new HttpException(
+ "Invalid approver for the transfer request",
+ HttpStatus.FORBIDDEN
+ );
+ }
+ if (transfer.isRetirement && transfer.toCompanyId != approver.companyId) {
+ throw new HttpException(
+ "Invalid approver for the retirement request",
+ HttpStatus.FORBIDDEN
+ );
}
- async queryProgrammeTransfers(query: QueryDto, abilityCondition: string): Promise {
- const resp = await this.programmeTransferViewRepo
- .createQueryBuilder('programme_transfer')
- .where(
- this.helperService.generateWhereSQL(
- query,
- this.helperService.parseMongoQueryToSQLWithTable("programme_transfer", abilityCondition)
- )
- )
- .orderBy(query?.sort?.key && `"${query?.sort?.key}"`, query?.sort?.order)
- .offset(query.size * query.page - query.size)
- .limit(query.size)
- .getManyAndCount();
-
- if (resp.length > 0) {
- resp[0] = resp[0].map( e => {
- e.certifier = e.certifier.length > 0 && e.certifier[0] === null ? []: e.certifier
- return e;
- })
- }
- return new DataListResponseDto(
- resp.length > 0 ? resp[0] : undefined,
- resp.length > 1 ? resp[1] : undefined
- );
- }
+ const receiver = await this.companyService.findByCompanyId(
+ transfer.toCompanyId
+ );
+ const giver = await this.companyService.findByCompanyId(
+ transfer.fromCompanyId
+ );
+
+ if (receiver.state === CompanyState.SUSPENDED) {
+ await this.companyService.companyTransferCancel(
+ transfer.toCompanyId,
+ `${transfer.comment}#${approver.companyId}#${approver.id}#${SystemActionType.SUSPEND_AUTO_CANCEL}#${receiver.name}`
+ );
+ throw new HttpException(
+ "Receive company suspended",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ if (giver.state === CompanyState.SUSPENDED) {
+ await this.companyService.companyTransferCancel(
+ transfer.fromCompanyId,
+ `${transfer.comment}#${approver.companyId}#${approver.id}#${SystemActionType.SUSPEND_AUTO_CANCEL}#${receiver.name}`
+ );
+ throw new HttpException(
+ "Credit sending company suspended",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- async transferApprove(req: ProgrammeTransferApprove, approver: User) {
- // TODO: Handle transaction, can happen
- console.log('Approver', approver)
- const transfer = await this.programmeTransferRepo.findOneBy({
+ if (transfer.status != TransferStatus.PROCESSING) {
+ const trq = await this.programmeTransferRepo
+ .update(
+ {
requestId: req.requestId,
+ status: TransferStatus.PENDING,
+ },
+ {
+ status: TransferStatus.PROCESSING,
+ txTime: new Date().getTime(),
+ }
+ )
+ .catch((err) => {
+ this.logger.error(err);
+ return err;
});
- if (!transfer) {
- throw new HttpException("Transfer request does not exist", HttpStatus.BAD_REQUEST)
- }
+ if (trq.affected <= 0) {
+ throw new HttpException(
+ "No pending transfer request found",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ }
- if (transfer.status == TransferStatus.CANCELLED) {
- throw new HttpException("Transfer request already cancelled", HttpStatus.BAD_REQUEST)
- }
+ const initiatorCompanyDetails = await this.companyService.findByCompanyId(
+ transfer.initiatorCompanyId
+ );
+
+ const transferResult = await this.doTransfer(
+ transfer,
+ `${this.getUserRef(approver)}#${receiver.companyId}#${receiver.name}#${
+ giver.companyId
+ }#${giver.name}`,
+ req.comment,
+ transfer.isRetirement
+ );
+
+ if(transferResult.statusCode === 200){
+ if (transfer.isRetirement) {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.fromCompanyId,
+ EmailTemplates.CREDIT_RETIREMENT_RECOGNITION,
+ {
+ credits: transfer.creditAmount,
+ country: transfer.toCompanyMeta.country,
+ },
+ 0,
+ transfer.programmeId
+ );
+ } else if (initiatorCompanyDetails.companyRole === CompanyRole.GOVERNMENT) {
+ await this.emailHelperService.sendEmailToGovernmentAdmins(
+ EmailTemplates.CREDIT_TRANSFER_GOV_ACCEPTED_TO_INITIATOR,
+ { credits: transfer.creditAmount },
+ transfer.programmeId,
+ approver.companyId
+ );
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.toCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_GOV_ACCEPTED_TO_RECEIVER,
+ {
+ credits: transfer.creditAmount,
+ government: initiatorCompanyDetails.name,
+ },
+ transfer.fromCompanyId,
+ transfer.programmeId
+ );
+ } else {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.toCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_ACCEPTED,
+ { credits: transfer.creditAmount },
+ approver.companyId,
+ transfer.programmeId
+ );
+ }
+ }
- if (transfer.status == TransferStatus.APPROVED || transfer.status == TransferStatus.RECOGNISED) {
- throw new HttpException("Transfer already approved", HttpStatus.BAD_REQUEST)
- }
+ return transferResult;
+ }
+
+ private async doTransfer(
+ transfer: ProgrammeTransfer,
+ user: string,
+ reason: string,
+ isRetirement: boolean
+ ) {
+ const programme = await this.programmeLedger.transferProgramme(
+ transfer,
+ user,
+ reason,
+ isRetirement
+ );
+
+ this.logger.log("Programme updated");
+ const result = await this.programmeTransferRepo
+ .update(
+ {
+ requestId: transfer.requestId,
+ },
+ {
+ status: transfer.isRetirement
+ ? TransferStatus.RECOGNISED
+ : TransferStatus.APPROVED,
+ txTime: new Date().getTime(),
+ }
+ )
+ .catch((err) => {
+ this.logger.error(err);
+ return err;
+ });
+
+ if (result.affected > 0) {
+ return new DataResponseDto(HttpStatus.OK, programme);
+ }
- if (!transfer.isRetirement && transfer.fromCompanyId != approver.companyId) {
- throw new HttpException("Invalid approver for the transfer request", HttpStatus.FORBIDDEN)
- }
- if (transfer.isRetirement && transfer.toCompanyId != approver.companyId) {
- throw new HttpException("Invalid approver for the retirement request", HttpStatus.FORBIDDEN)
- }
+ throw new HttpException(
+ "Internal error on status updating",
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ async transferCancel(req: ProgrammeTransferCancel, requester: User) {
+ this.logger.log(
+ `Programme transfer cancel by ${requester.companyId}-${
+ requester.id
+ } received ${JSON.stringify(req)}`
+ );
+
+ const transfer = await this.programmeTransferRepo.findOneBy({
+ requestId: req.requestId,
+ });
+
+ if (!transfer) {
+ throw new HttpException(
+ "Transfer request does not exist",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const receiver = await this.companyService.findByCompanyId(
- transfer.toCompanyId
+ if (transfer.status != TransferStatus.PENDING) {
+ throw new HttpException(
+ "Transfer already processed",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ const result = await this.programmeTransferRepo
+ .update(
+ {
+ requestId: req.requestId,
+ status: TransferStatus.PENDING,
+ },
+ {
+ status: TransferStatus.CANCELLED,
+ txTime: new Date().getTime(),
+ txRef: `${req.comment}#${requester.companyId}#${requester.id}`,
+ }
+ )
+ .catch((err) => {
+ this.logger.error(err);
+ return err;
+ });
+
+ if (result.affected > 0) {
+ const initiatorCompanyDetails = await this.companyService.findByCompanyId(
+ transfer.initiatorCompanyId
+ );
+ if (transfer.isRetirement) {
+ await this.emailHelperService.sendEmailToGovernmentAdmins(
+ EmailTemplates.CREDIT_RETIREMENT_CANCEL,
+ {
+ credits: transfer.creditAmount,
+ organisationName: initiatorCompanyDetails.name,
+ country: transfer.toCompanyMeta.country,
+ },
+ transfer.programmeId
+ );
+ } else if (
+ initiatorCompanyDetails.companyRole === CompanyRole.GOVERNMENT
+ ) {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.fromCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_GOV_CANCELLATION,
+ {
+ credits: transfer.creditAmount,
+ government: initiatorCompanyDetails.name,
+ },
+ transfer.toCompanyId,
+ transfer.programmeId
);
- const giver = await this.companyService.findByCompanyId(
- transfer.fromCompanyId
+ } else {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.fromCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_CANCELLATION,
+ { credits: transfer.creditAmount },
+ transfer.initiatorCompanyId,
+ transfer.programmeId
);
+ }
+ return new BasicResponseDto(HttpStatus.OK, "Successfully cancelled");
+ }
+ return new BasicResponseDto(
+ HttpStatus.BAD_REQUEST,
+ "Transfer request does not exist in the giv"
+ );
+ }
+
+ async transferRequest(req: ProgrammeTransferRequest, requester: User) {
+ this.logger.log(
+ `Programme transfer request by ${requester.companyId}-${
+ requester.id
+ } received ${JSON.stringify(req)}`
+ );
+
+ // TODO: Move this to casl factory
+ // if (requester.role == Role.ViewOnly) {
+ // throw new HttpException("View only user cannot create requests", HttpStatus.FORBIDDEN)
+ // }
+
+ // if (![CompanyRole.GOVERNMENT, CompanyRole.PROGRAMME_DEVELOPER].includes(requester.companyRole)) {
+ // throw new HttpException("Unsupported company role", HttpStatus.FORBIDDEN)
+ // }
+
+ if (
+ req.companyCredit &&
+ req.companyCredit.reduce((a, b) => a + b, 0) <= 0
+ ) {
+ throw new HttpException(
+ "Total Amount should be greater than 0",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (receiver.state === CompanyState.SUSPENDED) {
- await this.companyService.companyTransferCancel(transfer.toCompanyId);
- throw new HttpException(
- "Receive company suspended",
+ if (req.fromCompanyIds.length > 1) {
+ if (!req.companyCredit) {
+ throw new HttpException(
+ "Company credit needs to define for multiple companies",
+ HttpStatus.BAD_REQUEST
+ );
+ } else if (req.fromCompanyIds.length != req.companyCredit.length) {
+ throw new HttpException(
+ "Invalid company credit for given companies",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ }
+
+ if (req.fromCompanyIds && req.companyCredit && req.fromCompanyIds.length != req.companyCredit.length) {
+ throw new HttpException(
+ "Invalid company credit for given from companies",
HttpStatus.BAD_REQUEST
);
- }
+ }
- if (giver.state === CompanyState.SUSPENDED) {
- await this.companyService.companyTransferCancel(
- transfer.fromCompanyId
- );
- throw new HttpException(
- "Credit sending company suspended",
- HttpStatus.BAD_REQUEST
- );
- }
+ const indexTo = req.fromCompanyIds.indexOf(req.toCompanyId);
+ if (indexTo >= 0 && req.companyCredit[indexTo] > 0) {
+ throw new HttpException(
+ "Cannot transfer credit within the same company",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (transfer.status != TransferStatus.PROCESSING) {
- const trq = await this.programmeTransferRepo.update({
- requestId: req.requestId,
- status: TransferStatus.PENDING
- }, {
- status: TransferStatus.PROCESSING
- }).catch((err) => {
- this.logger.error(err);
- return err;
- });
-
- if (trq.affected <= 0) {
- throw new HttpException("No pending transfer request found", HttpStatus.BAD_REQUEST)
- }
- }
+ const programme = await this.programmeLedger.getProgrammeById(
+ req.programmeId
+ );
- return await this.doTransfer(transfer, `${this.getUserRef(approver)}#${receiver.companyId}#${receiver.name}`, req.comment, transfer.isRetirement)
+ if (!programme) {
+ throw new HttpException(
+ "Programme does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
+ this.logger.verbose(`Transfer programme ${JSON.stringify(programme)}`);
- private async doTransfer(transfer: ProgrammeTransfer, user: string, reason: string, isRetirement: boolean) {
- const programme = await this.programmeLedger.transferProgramme(transfer, user, reason, isRetirement);
+ if (programme.currentStage != ProgrammeStage.AUTHORISED) {
+ throw new HttpException(
+ "Programme is not in credit issued state",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ // if (programme.creditBalance - (programme.creditFrozen ? programme.creditFrozen.reduce((a, b) => a + b, 0) : 0) < req.creditAmount) {
+ // throw new HttpException("Not enough balance for the transfer", HttpStatus.BAD_REQUEST)
+ // }
+ if (
+ requester.companyRole != CompanyRole.GOVERNMENT &&
+ ![...req.fromCompanyIds, req.toCompanyId].includes(requester.companyId)
+ ) {
+ throw new HttpException(
+ "Cannot initiate transfers for other companies",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- this.logger.log('Programme updated');
- const result = await this.programmeTransferRepo.update({
- requestId: transfer.requestId
- }, {
- status: transfer.isRetirement ? TransferStatus.RECOGNISED : TransferStatus.APPROVED
- }).catch((err) => {
- this.logger.error(err);
- return err;
- });
+ if (!req.fromCompanyIds) {
+ req.fromCompanyIds = programme.companyId;
+ }
+ if (!programme.creditOwnerPercentage) {
+ programme.creditOwnerPercentage = [100];
+ }
+ if (!req.companyCredit) {
+ req.companyCredit = programme.creditOwnerPercentage.map(
+ (p, i) =>
+ (programme.creditBalance * p) / 100 -
+ (programme.creditFrozen ? programme.creditFrozen[i] : 0)
+ );
+ }
- if (result.affected > 0) {
- return new DataResponseDto(HttpStatus.OK, programme);
- }
+ const requestedCompany = await this.companyService.findByCompanyId(
+ requester.companyId
+ );
+
+ const allTransferList: ProgrammeTransfer[] = [];
+ const autoApproveTransferList: ProgrammeTransfer[] = [];
+ const ownershipMap = {};
+ const frozenCredit = {};
- throw new HttpException("Internal error on status updating", HttpStatus.INTERNAL_SERVER_ERROR)
+ for (const i in programme.companyId) {
+ ownershipMap[programme.companyId[i]] = programme.creditOwnerPercentage[i];
+ if (programme.creditFrozen) {
+ frozenCredit[programme.companyId[i]] = programme.creditFrozen[i];
+ }
}
- async transferCancel(req: ProgrammeTransferCancel, requester: User) {
- this.logger.log(`Programme transfer cancel by ${requester.companyId}-${requester.id} received ${JSON.stringify(req)}`)
+ const hostAddress = this.configService.get("host");
+
+ const fromCompanyListMap = {};
+ for (const j in req.fromCompanyIds) {
+ const fromCompanyId = req.fromCompanyIds[j];
+ this.logger.log(
+ `Transfer request from ${fromCompanyId} to programme owned by ${programme.companyId}`
+ );
+ const fromCompany = await this.companyService.findByCompanyId(
+ fromCompanyId
+ );
+ fromCompanyListMap[fromCompanyId] = fromCompany;
+
+ if (!programme.companyId.includes(fromCompanyId)) {
+ throw new HttpException(
+ "From company mentioned in the request does own the programme",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const transfer = await this.programmeTransferRepo.findOneBy({
- requestId: req.requestId,
- });
-
- if (!transfer) {
- throw new HttpException("Transfer request does not exist", HttpStatus.BAD_REQUEST)
- }
+ console.log(
+ programme.creditBalance,
+ ownershipMap[fromCompanyId],
+ frozenCredit[fromCompanyId]
+ );
+ const companyAvailableCredit =
+ (programme.creditBalance * ownershipMap[fromCompanyId]) / 100 -
+ (frozenCredit[fromCompanyId] ? frozenCredit[fromCompanyId] : 0);
+
+ let transferCompanyCredit;
+ if (req.fromCompanyIds.length == 1 && !req.companyCredit) {
+ transferCompanyCredit = companyAvailableCredit;
+ } else {
+ transferCompanyCredit = req.companyCredit[j];
+ }
- if (transfer.status != TransferStatus.PENDING) {
- throw new HttpException("Transfer already processed", HttpStatus.BAD_REQUEST)
- }
+ if (companyAvailableCredit < transferCompanyCredit) {
+ throw new HttpException(
+ `Company ${fromCompany.name} does not have enough balance for the transfer. Available: ${companyAvailableCredit}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const result = await this.programmeTransferRepo.update({
- requestId: req.requestId,
- status: TransferStatus.PENDING
- }, {
- status: TransferStatus.CANCELLED
- }).catch((err) => {
- this.logger.error(err);
- return err;
- });
+ if (transferCompanyCredit == 0) {
+ continue;
+ }
- if (result.affected > 0) {
- return new BasicResponseDto(HttpStatus.OK, "Successfully cancelled");
- }
- return new BasicResponseDto(HttpStatus.BAD_REQUEST, "Transfer request does not exist in the giv");
+ const transfer = new ProgrammeTransfer();
+ transfer.programmeId = req.programmeId;
+ transfer.fromCompanyId = fromCompanyId;
+ transfer.toCompanyId = req.toCompanyId;
+ transfer.initiator = requester.id;
+ transfer.initiatorCompanyId = requester.companyId;
+ transfer.txTime = new Date().getTime();
+ transfer.createdTime = transfer.txTime;
+ transfer.comment = req.comment;
+ transfer.creditAmount = transferCompanyCredit;
+ transfer.toAccount = req.toAccount;
+ transfer.isRetirement = false;
+
+ if (requester.companyId != fromCompanyId) {
+ transfer.status = TransferStatus.PENDING;
+ } else {
+ transfer.status = TransferStatus.PROCESSING;
+ autoApproveTransferList.push(transfer);
+ }
+ allTransferList.push(transfer);
+ }
+ const results = await this.programmeTransferRepo.insert(allTransferList);
+ console.log(results);
+ for (const i in allTransferList) {
+ allTransferList[i].requestId = results.identifiers[i].requestId;
}
- async transferRequest(req: ProgrammeTransferRequest, requester: User) {
- this.logger.log(`Programme transfer request by ${requester.companyId}-${requester.id} received ${JSON.stringify(req)}`)
-
- // TODO: Move this to casl factory
- // if (requester.role == Role.ViewOnly) {
- // throw new HttpException("View only user cannot create requests", HttpStatus.FORBIDDEN)
- // }
+ let updateProgramme = undefined;
+ for (const trf of autoApproveTransferList) {
+ this.logger.log(`Credit send received ${trf}`);
+ const toCompany = await this.companyService.findByCompanyId(
+ trf.toCompanyId
+ );
+ console.log("To Company", toCompany);
+ updateProgramme = (
+ await this.doTransfer(
+ trf,
+ `${this.getUserRef(requester)}#${toCompany.companyId}#${
+ toCompany.name
+ }#${fromCompanyListMap[trf.fromCompanyId].companyId}#${
+ fromCompanyListMap[trf.fromCompanyId].name
+ }`,
+ req.comment,
+ false
+ )
+ ).data;
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ trf.toCompanyId,
+ EmailTemplates.CREDIT_SEND_DEVELOPER,
+ {
+ organisationName: requestedCompany.name,
+ credits: trf.creditAmount,
+ programmeName: programme.title,
+ serialNumber: programme.serialNo,
+ pageLink: hostAddress + "/creditTransfers/viewAll",
+ }
+ );
+ }
+ if (updateProgramme) {
+ return new DataResponseDto(HttpStatus.OK, updateProgramme);
+ }
- // if (![CompanyRole.GOVERNMENT, CompanyRole.PROGRAMME_DEVELOPER].includes(requester.companyRole)) {
- // throw new HttpException("Unsupported company role", HttpStatus.FORBIDDEN)
- // }
+ allTransferList.forEach(async (transfer) => {
+ if (requester.companyRole === CompanyRole.GOVERNMENT) {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.fromCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_GOV,
+ {
+ credits: transfer.creditAmount,
+ programmeName: programme.title,
+ serialNumber: programme.serialNo,
+ pageLink: hostAddress + "/creditTransfers/viewAll",
+ government: requestedCompany.name,
+ },
+ transfer.toCompanyId
+ );
+ } else if (requester.companyId != transfer.fromCompanyId) {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ transfer.fromCompanyId,
+ EmailTemplates.CREDIT_TRANSFER_REQUISITIONS,
+ {
+ organisationName: requestedCompany.name,
+ credits: transfer.creditAmount,
+ programmeName: programme.title,
+ serialNumber: programme.serialNo,
+ pageLink: hostAddress + "/creditTransfers/viewAll",
+ }
+ );
+ }
+ });
+
+ return new DataListResponseDto(allTransferList, allTransferList.length);
+ }
+
+ async create(programmeDto: ProgrammeDto): Promise {
+ this.logger.verbose("ProgrammeDTO received", programmeDto);
+ const programme: Programme = this.toProgramme(programmeDto);
+ this.logger.verbose("Programme create", programme);
+
+ if (
+ programmeDto.proponentTaxVatId.length > 1 &&
+ (!programmeDto.proponentPercentage ||
+ programmeDto.proponentPercentage.length !=
+ programmeDto.proponentTaxVatId.length)
+ ) {
+ throw new HttpException(
+ "Proponent percentage must defined for each proponent tax id",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (req.companyCredit && req.companyCredit.reduce((a, b) => a + b, 0) <= 0) {
- throw new HttpException("Total Amount should be greater than 0", HttpStatus.BAD_REQUEST)
- }
+ if (
+ programmeDto.proponentPercentage &&
+ programmeDto.proponentTaxVatId.length !=
+ programmeDto.proponentPercentage.length
+ ) {
+ throw new HttpException(
+ "Proponent percentage and number of tax ids does not match",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (req.fromCompanyIds.length > 1 ) {
- if (!req.companyCredit) {
- throw new HttpException("Company credit needs to define for multiple companies", HttpStatus.BAD_REQUEST)
- } else if (req.fromCompanyIds.length != req.companyCredit.length){
- throw new HttpException("Invalid company credit for given companies", HttpStatus.BAD_REQUEST)
- }
- }
+ if (
+ programmeDto.proponentPercentage &&
+ programmeDto.proponentPercentage.reduce((a, b) => a + b, 0) != 100
+ ) {
+ throw new HttpException(
+ "Proponent percentage sum must be equals to 100",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const indexTo = req.fromCompanyIds.indexOf(req.toCompanyId);
- if (indexTo >= 0 && req.companyCredit[indexTo] > 0) {
- throw new HttpException("Cannot transfer credit within the same company", HttpStatus.BAD_REQUEST)
- }
+ if (
+ programmeDto.proponentTaxVatId.length !==
+ new Set(programmeDto.proponentTaxVatId).size
+ ) {
+ throw new HttpException(
+ "Proponent tax id cannot be duplicated",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const programme = await this.programmeLedger.getProgrammeById(req.programmeId);
+ const companyIds = [];
+ const companyNames = [];
+ for (const taxId of programmeDto.proponentTaxVatId) {
+ const projectCompany = await this.companyService.findByTaxId(taxId);
+ if (!projectCompany) {
+ throw new HttpException(
+ "Proponent tax id does not exist in the system",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (!programme) {
- throw new HttpException("Programme does not exist", HttpStatus.BAD_REQUEST)
- }
- this.logger.verbose(`Transfer programme ${JSON.stringify(programme)}`)
+ if (projectCompany.companyRole != CompanyRole.PROGRAMME_DEVELOPER) {
+ throw new HttpException(
+ "Proponent is not a programme developer",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (programme.currentStage != ProgrammeStage.AUTHORISED) {
- throw new HttpException("Programme is not in credit issued state", HttpStatus.BAD_REQUEST)
- }
- // if (programme.creditBalance - (programme.creditFrozen ? programme.creditFrozen.reduce((a, b) => a + b, 0) : 0) < req.creditAmount) {
- // throw new HttpException("Not enough balance for the transfer", HttpStatus.BAD_REQUEST)
- // }
- // if (requester.companyRole != CompanyRole.GOVERNMENT && programme.companyId.includes(requester.companyId)) {
- // throw new HttpException("Cannot initiate transfers for already owned programmes", HttpStatus.BAD_REQUEST)
- // }
-
- if (!req.fromCompanyIds) {
- req.fromCompanyIds = programme.companyId;
- }
- if (!programme.creditOwnerPercentage) {
- programme.creditOwnerPercentage = [100]
- }
- if (!req.companyCredit) {
- req.companyCredit = programme.creditOwnerPercentage.map((p, i) => (programme.creditBalance*p/100 - (programme.creditFrozen ? programme.creditFrozen[i]: 0)));
- }
+ companyIds.push(projectCompany.companyId);
+ companyNames.push(projectCompany.name);
+ }
- const requestedCompany = await this.companyService.findByCompanyId(requester.companyId);
-
- const allTransferList: ProgrammeTransfer[] = []
- const autoApproveTransferList: ProgrammeTransfer[] = []
- const ownershipMap = {}
- const frozenCredit = {}
-
- for (const i in programme.companyId) {
- ownershipMap[programme.companyId[i]] = programme.creditOwnerPercentage[i]
- if (programme.creditFrozen) {
- frozenCredit[programme.companyId[i]] = programme.creditFrozen[i]
- }
- }
-
- for (const j in req.fromCompanyIds) {
- const fromCompanyId = req.fromCompanyIds[j]
- this.logger.log(`Transfer request from ${fromCompanyId} to programme owned by ${programme.companyId}`)
- const fromCompany = await this.companyService.findByCompanyId(fromCompanyId);
-
- if (!programme.companyId.includes(fromCompanyId)) {
- throw new HttpException("From company mentioned in the request does own the programme", HttpStatus.BAD_REQUEST)
- }
-
- console.log(programme.creditBalance, ownershipMap[fromCompanyId], frozenCredit[fromCompanyId])
- const companyAvailableCredit = (programme.creditBalance * ownershipMap[fromCompanyId] / 100) - (frozenCredit[fromCompanyId] ? frozenCredit[fromCompanyId] : 0);
-
- let transferCompanyCredit;
- if (req.fromCompanyIds.length == 1 && !req.companyCredit) {
- transferCompanyCredit = companyAvailableCredit;
- } else {
- transferCompanyCredit = req.companyCredit[j];
- }
-
- if (companyAvailableCredit < transferCompanyCredit) {
- throw new HttpException(`Company ${fromCompany.name} does not have enough balance for the transfer. Available: ${companyAvailableCredit}`, HttpStatus.BAD_REQUEST)
- }
-
- if (transferCompanyCredit == 0) {
- continue;
- }
-
- const transfer = new ProgrammeTransfer();
- transfer.programmeId = req.programmeId;
- transfer.fromCompanyId = fromCompanyId;
- transfer.toCompanyId = req.toCompanyId;
- transfer.initiator = requester.id;
- transfer.initiatorCompanyId = requester.companyId;
- transfer.txTime = new Date().getTime()
- transfer.comment = req.comment;
- transfer.creditAmount = transferCompanyCredit;
- transfer.toAccount = req.toAccount;
- transfer.isRetirement = false;
-
- if (requester.companyId != fromCompanyId) {
- transfer.status = TransferStatus.PENDING;
- await this.emailService.sendEmail(
- fromCompany.email,
- EmailTemplates.TRANSFER_REQUEST,
- {
- "name": fromCompany.name,
- "requestedCompany": requestedCompany.name,
- "credits": transfer.creditAmount,
- "serialNo": programme.serialNo,
- "programmeName": programme.title
- });
- } else {
- transfer.status = TransferStatus.PROCESSING;
- autoApproveTransferList.push(transfer);
- }
- allTransferList.push(transfer);
- }
- const results = await this.programmeTransferRepo.insert(allTransferList)
- console.log(results)
- for (const i in allTransferList) {
- allTransferList[i].requestId = results.identifiers[i].requestId;
- }
+ programme.programmeId = await this.counterService.incrementCount(
+ CounterType.PROGRAMME,
+ 3
+ );
+ programme.countryCodeA2 = this.configService.get("systemCountry");
+ const constants = await this.getLatestConstant(
+ programmeDto.typeOfMitigation
+ );
+
+ const req = await this.getCreditRequest(programmeDto, constants);
+ try {
+ programme.creditEst = Math.round(await calculateCredit(req));
+ } catch (err) {
+ this.logger.log(`Credit calculate failed ${err.message}`);
+ throw new HttpException(err.message, HttpStatus.BAD_REQUEST);
+ }
- let updateProgramme = undefined;
- for (const trf of autoApproveTransferList) {
- this.logger.log(`Credit send received ${trf}`)
- const toCompany = await this.companyService.findByCompanyId(trf.toCompanyId);
- console.log('To Company', toCompany)
- updateProgramme = (await this.doTransfer(trf, `${this.getUserRef(requester)}#${toCompany.companyId}#${toCompany.name}`, req.comment, false)).data;
- }
- if (updateProgramme) {
- return new DataResponseDto(HttpStatus.OK, updateProgramme)
- }
- return new DataListResponseDto(allTransferList, allTransferList.length)
+ if (programme.creditEst <= 0) {
+ throw new HttpException(
+ "Not enough credits to create the programme",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ // programme.creditBalance = programme.creditIssued;
+ // programme.creditChange = programme.creditIssued;
+ programme.programmeProperties.creditYear = new Date(
+ programme.startTime * 1000
+ ).getFullYear();
+ programme.constantVersion = constants
+ ? String(constants.version)
+ : "default";
+ programme.currentStage = ProgrammeStage.AWAITING_AUTHORIZATION;
+ programme.companyId = companyIds;
+ programme.txTime = new Date().getTime();
+ if (programme.proponentPercentage) {
+ programme.creditOwnerPercentage = programme.proponentPercentage;
+ }
+ programme.createdTime = programme.txTime;
+ if (!programme.creditUnit) {
+ programme.creditUnit = this.configService.get("defaultCreditUnit");
}
- async create(programmeDto: ProgrammeDto): Promise {
- this.logger.verbose('ProgrammeDTO received', programmeDto)
- const programme: Programme = this.toProgramme(programmeDto);
- this.logger.verbose('Programme create', programme)
+ let orgNamesList = "";
+ if (companyNames.length > 1) {
+ const lastItem = companyNames.pop();
+ orgNamesList = companyNames.join(",") + " and " + lastItem;
+ } else {
+ orgNamesList = companyNames[0];
+ }
+
+ if (programme.companyId.length === 1 && !programme.proponentPercentage) {
+ programme.proponentPercentage = [100];
+ programme.creditOwnerPercentage = [100];
+ }
+ const savedProgramme = await this.programmeLedger.createProgramme(programme);
+ if(savedProgramme){
+ const hostAddress = this.configService.get("host");
+ await this.emailHelperService.sendEmailToGovernmentAdmins(
+ EmailTemplates.PROGRAMME_CREATE,
+ {
+ organisationName: orgNamesList,
+ programmePageLink:
+ hostAddress + `/programmeManagement/view?id=${programme.programmeId}`,
+ }
+ );
+ }
- if (programmeDto.proponentTaxVatId.length > 1 && (!programmeDto.proponentPercentage || programmeDto.proponentPercentage.length != programmeDto.proponentTaxVatId.length)) {
- throw new HttpException("Proponent percentage must defined for each proponent tax id", HttpStatus.BAD_REQUEST)
- }
+ return savedProgramme;
+ }
+
+ async query(
+ query: QueryDto,
+ abilityCondition: string
+ ): Promise {
+ const skip = query.size * query.page - query.size;
+ let resp = await this.programmeViewRepo
+ .createQueryBuilder("programme")
+ .where(
+ this.helperService.generateWhereSQL(
+ query,
+ this.helperService.parseMongoQueryToSQLWithTable(
+ "programme",
+ abilityCondition
+ ),
+ "programme"
+ )
+ )
+ .orderBy(
+ query?.sort?.key && `"programme"."${query?.sort?.key}"`,
+ query?.sort?.order,
+ query?.sort?.nullFirst !== undefined
+ ? query?.sort?.nullFirst === true
+ ? "NULLS FIRST"
+ : "NULLS LAST"
+ : undefined
+ )
+ .offset(skip)
+ .limit(query.size)
+ .getManyAndCount();
+
+ if (resp.length > 0) {
+ resp[0] = resp[0].map((e) => {
+ e.certifier =
+ e.certifier.length > 0 && e.certifier[0] === null ? [] : e.certifier;
+ e.company =
+ e.company.length > 0 && e.company[0] === null ? [] : e.company;
+ return e;
+ });
+ }
- if (programmeDto.proponentPercentage && programmeDto.proponentTaxVatId.length != programmeDto.proponentPercentage.length) {
- throw new HttpException("Proponent percentage and number of tax ids does not match", HttpStatus.BAD_REQUEST)
- }
+ return new DataListResponseDto(
+ resp.length > 0 ? resp[0] : undefined,
+ resp.length > 1 ? resp[1] : undefined
+ );
+ }
- if (programmeDto.proponentPercentage && programmeDto.proponentPercentage.reduce((a, b) => a + b, 0) != 100) {
- throw new HttpException("Proponent percentage sum must be equals to 100", HttpStatus.BAD_REQUEST)
+ async getProgrammeEvents(programmeId: string, user: User): Promise {
+ const resp = await this.programmeLedger.getProgrammeHistory(programmeId);
+ if (resp == null) {
+ return [];
+ }
+ for (const el of resp) {
+ const refs = this.getCompanyIdAndUserIdFromRef(el.data.txRef);
+ if (
+ refs &&
+ (user.companyRole === CompanyRole.GOVERNMENT ||
+ Number(refs?.companyId) === Number(user.companyId))
+ ) {
+ el.data["userName"] = await this.getUserName(refs.id);
+ }
+ }
+ return resp;
+ }
+
+ async updateCustomConstants(
+ customConstantType: TypeOfMitigation,
+ constants: ConstantUpdateDto
+ ) {
+ let config;
+ if (customConstantType == TypeOfMitigation.AGRICULTURE) {
+ config = new AgricultureConstants();
+ const recv = instanceToPlain(constants.agricultureConstants);
+ for (const key in recv) {
+ if (recv.hasOwnProperty(key) && recv[key] != undefined) {
+ config[key] = recv[key];
}
-
- if (programmeDto.proponentTaxVatId.length !== new Set(programmeDto.proponentTaxVatId).size) {
- throw new HttpException("Proponent tax id cannot be duplicated", HttpStatus.BAD_REQUEST)
+ }
+ } else if (customConstantType == TypeOfMitigation.SOLAR) {
+ config = new SolarConstants();
+ const recv = instanceToPlain(constants.solarConstants);
+ for (const key in recv) {
+ if (recv.hasOwnProperty(key) && recv[key] != undefined) {
+ config[key] = recv[key];
}
+ }
+ }
- const companyIds = []
- for (const taxId of programmeDto.proponentTaxVatId) {
- const projectCompany = await this.companyService.findByTaxId(taxId);
- if (!projectCompany) {
- throw new HttpException("Proponent tax id does not exist in the system", HttpStatus.BAD_REQUEST)
- }
-
- if (projectCompany.companyRole != CompanyRole.PROGRAMME_DEVELOPER) {
- throw new HttpException("Proponent is not a programme developer", HttpStatus.BAD_REQUEST)
- }
-
- companyIds.push(projectCompany.companyId)
- }
+ const existing = await this.getLatestConstant(customConstantType);
+ if (existing && JSON.stringify(existing.data) == JSON.stringify(config)) {
+ throw new HttpException(
+ "Not difference in the config from the previous version",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ const resp = await this.constantRepo.save({
+ id: customConstantType,
+ data: config,
+ });
+ return new DataResponseDto(HttpStatus.OK, resp);
+ }
+
+ async getLatestConstant(customConstantType: TypeOfMitigation) {
+ return await this.constantRepo.findOne({
+ where: [{ id: customConstantType }],
+ order: { version: "DESC" },
+ });
+ }
+
+ async certify(req: ProgrammeCertify, add: boolean, user: User) {
+ this.logger.log(
+ `Programme ${req.programmeId} certification received by ${user.id}`
+ );
+
+ if (add && user.companyRole != CompanyRole.CERTIFIER) {
+ throw new HttpException(
+ "Programme certification can perform only by certifier",
+ HttpStatus.FORBIDDEN
+ );
+ }
+ if (
+ !add &&
+ ![CompanyRole.CERTIFIER, CompanyRole.GOVERNMENT].includes(
+ user.companyRole
+ )
+ ) {
+ throw new HttpException(
+ "Programme certification revoke can perform only by certifier or government",
+ HttpStatus.FORBIDDEN
+ );
+ }
- programme.programmeId = (await this.counterService.incrementCount(CounterType.PROGRAMME, 3))
- programme.countryCodeA2 = this.configService.get('systemCountry');
- const constants = await this.getLatestConstant(programmeDto.typeOfMitigation)
+ let certifierId;
+ if (user.companyRole === CompanyRole.GOVERNMENT) {
+ if (!req.certifierId) {
+ throw new HttpException(
+ "certifierId required for government user",
+ HttpStatus.FORBIDDEN
+ );
+ }
+ certifierId = req.certifierId;
+ } else {
+ certifierId = user.companyId;
+ }
- const req = await this.getCreditRequest(programmeDto, constants);
- try {
- programme.creditEst = Math.round(await calculateCredit(req));
- } catch (err) {
- this.logger.log(`Credit calculate failed ${err.message}`)
- throw new HttpException(err.message, HttpStatus.BAD_REQUEST)
- }
+ const updated = await this.programmeLedger.updateCertifier(
+ req.programmeId,
+ certifierId,
+ add,
+ this.getUserRefWithRemarks(user, req.comment)
+ );
+ updated.company = await this.companyRepo.find({
+ where: { companyId: In(updated.companyId) },
+ });
+ if (updated && updated.certifierId && updated.certifierId.length > 0) {
+ updated.certifier = await this.companyRepo.find({
+ where: { companyId: In(updated.certifierId) },
+ });
+ }
- if (programme.creditEst <= 0) {
- throw new HttpException("Not enough credits to create the programme", HttpStatus.BAD_REQUEST)
- }
- // programme.creditBalance = programme.creditIssued;
- // programme.creditChange = programme.creditIssued;
- programme.programmeProperties.creditYear = new Date(programme.startTime * 1000).getFullYear()
- programme.constantVersion = constants ? String(constants.version) : "default"
- programme.currentStage = ProgrammeStage.AWAITING_AUTHORIZATION;
- programme.companyId = companyIds;
- programme.txTime = new Date().getTime();
- if (programme.proponentPercentage){
- programme.creditOwnerPercentage = programme.proponentPercentage
- }
- programme.createdTime = programme.txTime;
- if (!programme.creditUnit) {
- programme.creditUnit = this.configService.get('defaultCreditUnit')
- }
+ if (add) {
+ await this.emailHelperService.sendEmailToProgrammeOwnerAdmins(
+ req.programmeId,
+ EmailTemplates.PROGRAMME_CERTIFICATION,
+ {},
+ user.companyId
+ );
+ } else {
+ if (user.companyRole === CompanyRole.GOVERNMENT) {
+ await this.emailHelperService.sendEmailToProgrammeOwnerAdmins(
+ req.programmeId,
+ EmailTemplates.PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_PROGRAMME,
+ {},
+ req.certifierId,
+ user.companyId
+ );
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ req.certifierId,
+ EmailTemplates.PROGRAMME_CERTIFICATION_REVOKE_BY_GOVT_TO_CERT,
+ {},
+ user.companyId,
+ req.programmeId
+ );
+ } else {
+ await this.emailHelperService.sendEmailToProgrammeOwnerAdmins(
+ req.programmeId,
+ EmailTemplates.PROGRAMME_CERTIFICATION_REVOKE_BY_CERT,
+ {},
+ user.companyId
+ );
+ }
+ }
- return await this.programmeLedger.createProgramme(programme);
- }
-
- async query(query: QueryDto, abilityCondition: string): Promise {
- const skip = (query.size * query.page) - query.size;
- let resp = (await this.programmeViewRepo.createQueryBuilder("programme")
- .where(this.helperService.generateWhereSQL(query, this.helperService.parseMongoQueryToSQLWithTable("programme", abilityCondition), "programme"))
- .orderBy(
- query?.sort?.key && `"programme"."${query?.sort?.key}"`,
- query?.sort?.order
- )
- .offset(skip)
- .limit(query.size)
- .getManyAndCount())
-
- if (resp.length > 0) {
- resp[0] = resp[0].map( e => {
- e.certifier = e.certifier.length > 0 && e.certifier[0] === null ? []: e.certifier
- e.company = e.company.length > 0 && e.company[0] === null ? []: e.company
- return e;
- })
- }
+ return new DataResponseDto(HttpStatus.OK, updated);
+ }
+
+ async retireProgramme(req: ProgrammeRetire, requester: User) {
+ this.logger.log(
+ `Programme ${req.programmeId} retiring Comment: ${req.comment} type: ${req.type}`
+ );
+
+ if (
+ req.companyCredit &&
+ req.companyCredit.reduce((a, b) => a + b, 0) <= 0
+ ) {
+ throw new HttpException(
+ "Total Amount should be greater than 0",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- return new DataListResponseDto(
- resp.length > 0 ? resp[0] : undefined,
- resp.length > 1 ? resp[1] : undefined
+ if (req.fromCompanyIds && req.fromCompanyIds.length > 1) {
+ if (
+ req.companyCredit &&
+ req.fromCompanyIds.length != req.companyCredit.length
+ ) {
+ throw new HttpException(
+ "Invalid company credit for given companies",
+ HttpStatus.BAD_REQUEST
);
+ }
}
- async getProgrammeEvents(programmeId: string, companyId: number): Promise {
- const resp = await this.programmeLedger.getProgrammeHistory(programmeId);
- // const comp = await this.companyService.findByCompanyId(companyId)
- // if (resp.length > 0 && comp.state == CompanyState.SUSPENDED) {
-
- // }
- return resp == null ? [] : resp;
- }
+ // if (req.type === RetireType.CROSS_BORDER && !req.toCompanyMeta.country) {
+ // throw new HttpException("Country is required for cross border retirement", HttpStatus.BAD_REQUEST)
+ // }
- async updateCustomConstants(customConstantType: TypeOfMitigation, constants: ConstantUpdateDto) {
- let config;
- if (customConstantType == TypeOfMitigation.AGRICULTURE) {
- config = new AgricultureConstants()
- const recv = instanceToPlain(constants.agricultureConstants)
- for (const key in recv) {
- if (recv.hasOwnProperty(key) && recv[key] != undefined) {
- config[key] = recv[key]
- }
- }
- }
- else if (customConstantType == TypeOfMitigation.SOLAR) {
- config = new SolarConstants()
- const recv = instanceToPlain(constants.solarConstants)
- for (const key in recv) {
- if (recv.hasOwnProperty(key) && recv[key] != undefined) {
- config[key] = recv[key]
- }
- }
- }
+ const programme = await this.programmeLedger.getProgrammeById(
+ req.programmeId
+ );
- const existing = await this.getLatestConstant(customConstantType);
- if (existing && JSON.stringify(existing.data) == JSON.stringify(config)) {
- throw new HttpException("Not difference in the config from the previous version", HttpStatus.BAD_REQUEST)
- }
- const resp = await this.constantRepo.save({
- id: customConstantType,
- data: config
- })
- return new DataResponseDto(HttpStatus.OK, resp);
+ if (!programme) {
+ throw new HttpException(
+ "Programme does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
+ this.logger.verbose(`Transfer programme ${JSON.stringify(programme)}`);
- async getLatestConstant(customConstantType: TypeOfMitigation) {
- return await this.constantRepo.findOne({
- where: [{ id: customConstantType }],
- order: { version: 'DESC' }
- });
+ if (programme.currentStage != ProgrammeStage.AUTHORISED) {
+ throw new HttpException(
+ "Programme is not in credit issued state",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ if (!programme.creditOwnerPercentage) {
+ programme.creditOwnerPercentage = [100];
}
+ const requestedCompany = await this.companyService.findByCompanyId(
+ requester.companyId
+ );
+ const toCompany = await this.companyService.findGovByCountry(
+ this.configService.get("systemCountry")
+ );
+
+ if (requestedCompany.companyRole != CompanyRole.GOVERNMENT) {
+ if (!req.fromCompanyIds) {
+ req.fromCompanyIds = [requester.companyId];
+ }
- async certify(req: ProgrammeCertify, add: boolean, user: User) {
- this.logger.log(`Programme ${req.programmeId} certification received by ${user.id}`)
+ if (!programme.companyId.includes(requester.companyId)) {
+ throw new HttpException(
+ "Credit retirement can initiate only the government or programme owner",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (add && user.companyRole != CompanyRole.CERTIFIER) {
- throw new HttpException("Programme certification can perform only by certifier", HttpStatus.FORBIDDEN)
- }
+ if (!req.fromCompanyIds.includes(requester.companyId)) {
+ throw new HttpException(
+ "Requester does not included in the from company id",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (!add && ![CompanyRole.CERTIFIER, CompanyRole.GOVERNMENT].includes(user.companyRole)) {
- throw new HttpException("Programme certification revoke can perform only by certifier or government", HttpStatus.FORBIDDEN)
- }
+ if (req.fromCompanyIds.length > 1) {
+ throw new HttpException(
+ "Does not allow to retire other company credits",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- let certifierId;
- if (user.companyRole === CompanyRole.GOVERNMENT) {
- if (!req.certifierId) {
- throw new HttpException("certifierId required for government user", HttpStatus.FORBIDDEN)
- }
- certifierId = req.certifierId
- } else {
- certifierId = user.companyId;
- }
+ if (req.type !== RetireType.CROSS_BORDER) {
+ throw new HttpException(
+ "Programme developer allowed to initiate only cross border transfers",
+ HttpStatus.BAD_REQUEST
+ );
+ }
- const updated = await this.programmeLedger.updateCertifier(req.programmeId, certifierId, add, this.getUserRef(user))
- updated.company = await this.companyRepo.find({
- where: { companyId: In(updated.companyId) },
- })
- if (updated && updated.certifierId && updated.certifierId.length > 0) {
- updated.certifier = await this.companyRepo.find({
- where: { companyId: In(updated.certifierId) },
- })
- }
- return new DataResponseDto(HttpStatus.OK, updated)
+ if (!req.companyCredit) {
+ const reqIndex = programme.companyId.indexOf(requester.companyId);
+ req.companyCredit = [
+ (programme.creditBalance *
+ programme.creditOwnerPercentage[reqIndex]) /
+ 100 -
+ (programme.creditFrozen ? programme.creditFrozen[reqIndex] : 0),
+ ];
+ }
+ } else {
+ if (!req.fromCompanyIds) {
+ req.fromCompanyIds = programme.companyId;
+ }
+ if (!req.companyCredit) {
+ req.companyCredit = programme.creditOwnerPercentage.map(
+ (p, i) =>
+ (programme.creditBalance * p) / 100 -
+ (programme.creditFrozen ? programme.creditFrozen[i] : 0)
+ );
+ }
}
- async retireProgramme(req: ProgrammeRetire, requester: User) {
- this.logger.log(`Programme ${req.programmeId} retiring Comment: ${req.comment} type: ${req.type}`)
-
- if (req.fromCompanyIds && req.fromCompanyIds.length > 1 ) {
- if (req.companyCredit && req.fromCompanyIds.length != req.companyCredit.length){
- throw new HttpException("Invalid company credit for given companies", HttpStatus.BAD_REQUEST)
- }
- }
+ const allTransferList: ProgrammeTransfer[] = [];
+ const autoApproveTransferList: ProgrammeTransfer[] = [];
+ const ownershipMap = {};
+ const frozenCredit = {};
- // if (req.type === RetireType.CROSS_BORDER && !req.toCompanyMeta.country) {
- // throw new HttpException("Country is required for cross border retirement", HttpStatus.BAD_REQUEST)
- // }
+ for (const i in programme.companyId) {
+ ownershipMap[programme.companyId[i]] = programme.creditOwnerPercentage[i];
+ if (programme.creditFrozen) {
+ frozenCredit[programme.companyId[i]] = programme.creditFrozen[i];
+ }
+ }
- const programme = await this.programmeLedger.getProgrammeById(req.programmeId);
+ const fromCompanyMap = {};
+ for (const j in req.fromCompanyIds) {
+ const fromCompanyId = req.fromCompanyIds[j];
+ this.logger.log(
+ `Retire request from ${fromCompanyId} to programme owned by ${programme.companyId}`
+ );
+ const fromCompany = await this.companyService.findByCompanyId(
+ fromCompanyId
+ );
+ fromCompanyMap[fromCompanyId] = fromCompany;
+ if (!programme.companyId.includes(fromCompanyId)) {
+ throw new HttpException(
+ "Retire request from company does own the programme",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ const companyAvailableCredit =
+ (programme.creditBalance * ownershipMap[fromCompanyId]) / 100 -
+ (frozenCredit[fromCompanyId] ? frozenCredit[fromCompanyId] : 0);
+
+ let transferCompanyCredit;
+ if (req.fromCompanyIds.length == 1 && !req.companyCredit) {
+ transferCompanyCredit = companyAvailableCredit;
+ } else {
+ transferCompanyCredit = req.companyCredit[j];
+ }
- if (!programme) {
- throw new HttpException("Programme does not exist", HttpStatus.BAD_REQUEST)
- }
- this.logger.verbose(`Transfer programme ${JSON.stringify(programme)}`)
+ if (
+ req.type != RetireType.CROSS_BORDER &&
+ transferCompanyCredit < companyAvailableCredit
+ ) {
+ throw new HttpException(
+ `Required to retire the full credit amount for the given retirement type`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (programme.currentStage != ProgrammeStage.AUTHORISED) {
- throw new HttpException("Programme is not in credit issued state", HttpStatus.BAD_REQUEST)
- }
-
+ if (companyAvailableCredit < transferCompanyCredit) {
+ throw new HttpException(
+ `Company ${fromCompany.name} does not have enough balance for the transfer. Available: ${companyAvailableCredit}`,
+ HttpStatus.BAD_REQUEST
+ );
+ }
- if (!req.fromCompanyIds) {
- req.fromCompanyIds = programme.companyId;
- }
- if (!programme.creditOwnerPercentage) {
- programme.creditOwnerPercentage = [100]
- }
- if (!req.companyCredit) {
- req.companyCredit = programme.creditOwnerPercentage.map((p, i) => (programme.creditBalance*p/100 - (programme.creditFrozen ? programme.creditFrozen[i]: 0)));
- }
+ if (transferCompanyCredit == 0) {
+ continue;
+ }
- const requestedCompany = await this.companyService.findByCompanyId(requester.companyId);
- const toCompany = await this.companyService.findGovByCountry(this.configService.get('systemCountry'))
+ const transfer = new ProgrammeTransfer();
+ transfer.programmeId = req.programmeId;
+ transfer.fromCompanyId = fromCompanyId;
+ transfer.toCompanyId = toCompany.companyId;
+ transfer.initiator = requester.id;
+ transfer.initiatorCompanyId = requester.companyId;
+ transfer.txTime = new Date().getTime();
+ transfer.createdTime = transfer.txTime;
+ transfer.comment = req.comment;
+ transfer.creditAmount = transferCompanyCredit;
+ transfer.toAccount =
+ req.type == RetireType.CROSS_BORDER ? "international" : "local";
+ transfer.isRetirement = true;
+ transfer.toCompanyMeta = req.toCompanyMeta;
+ transfer.retirementType = req.type;
+ // await this.programmeTransferRepo.save(transfer);
+
+ const hostAddress = this.configService.get("host");
+ if (requester.companyId != toCompany.companyId) {
+ transfer.status = TransferStatus.PENDING;
+ await this.emailHelperService.sendEmailToGovernmentAdmins(
+ EmailTemplates.CREDIT_RETIREMENT_BY_DEV,
+ {
+ credits: transfer.creditAmount,
+ programmeName: programme.title,
+ serialNumber: programme.serialNo,
+ organisationName: fromCompany.name,
+ pageLink: hostAddress + "/creditTransfers/viewAll",
+ }
+ );
+ } else {
+ transfer.status = TransferStatus.PROCESSING;
+ autoApproveTransferList.push(transfer);
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ fromCompany.companyId,
+ EmailTemplates.CREDIT_RETIREMENT_BY_GOV,
+ {
+ credits: transfer.creditAmount,
+ programmeName: programme.title,
+ serialNumber: programme.serialNo,
+ government: toCompany.name,
+ reason: req.comment,
+ pageLink: hostAddress + "/creditTransfers/viewAll",
+ }
+ );
+ }
+ allTransferList.push(transfer);
+ }
+ const results = await this.programmeTransferRepo.insert(allTransferList);
+ console.log(results);
+ for (const i in allTransferList) {
+ allTransferList[i].requestId = results.identifiers[i].requestId;
+ }
- if (requestedCompany.companyRole != CompanyRole.GOVERNMENT) {
- if (!programme.companyId.includes(requester.companyId)) {
- throw new HttpException("Credit retirement can initiate only the government or programme owner", HttpStatus.BAD_REQUEST)
- }
- if (req.type !== RetireType.CROSS_BORDER) {
- throw new HttpException("Programme developer allowed to initiate only cross border transfers", HttpStatus.BAD_REQUEST)
- }
- }
-
-
- const allTransferList: ProgrammeTransfer[] = []
- const autoApproveTransferList: ProgrammeTransfer[] = []
- const ownershipMap = {}
- const frozenCredit = {}
-
- for (const i in programme.companyId) {
- ownershipMap[programme.companyId[i]] = programme.creditOwnerPercentage[i]
- if (programme.creditFrozen) {
- frozenCredit[programme.companyId[i]] = programme.creditFrozen[i]
- }
- }
-
- for (const j in req.fromCompanyIds) {
- const fromCompanyId = req.fromCompanyIds[j]
- this.logger.log(`Retire request from ${fromCompanyId} to programme owned by ${programme.companyId}`)
- const fromCompany = await this.companyService.findByCompanyId(fromCompanyId);
-
- if (!programme.companyId.includes(fromCompanyId)) {
- throw new HttpException("Retire request from company does own the programme", HttpStatus.BAD_REQUEST)
- }
- const companyAvailableCredit = (programme.creditBalance * ownershipMap[fromCompanyId] / 100) - (frozenCredit[fromCompanyId] ? frozenCredit[fromCompanyId] : 0);
-
- let transferCompanyCredit;
- if (req.fromCompanyIds.length == 1 && !req.companyCredit) {
- transferCompanyCredit = companyAvailableCredit;
- } else {
- transferCompanyCredit = req.companyCredit[j];
- }
-
- if (companyAvailableCredit < transferCompanyCredit) {
- throw new HttpException(`Company ${fromCompany.name} does not have enough balance for the transfer. Available: ${companyAvailableCredit}`, HttpStatus.BAD_REQUEST)
- }
-
- if (transferCompanyCredit == 0) {
- continue;
- }
-
- const transfer = new ProgrammeTransfer();
- transfer.programmeId = req.programmeId;
- transfer.fromCompanyId = fromCompanyId;
- transfer.toCompanyId = toCompany.companyId;
- transfer.initiator = requester.id;
- transfer.initiatorCompanyId = requester.companyId;
- transfer.txTime = new Date().getTime()
- transfer.comment = req.comment;
- transfer.creditAmount = transferCompanyCredit;
- transfer.toAccount = req.type == RetireType.CROSS_BORDER ? 'international': 'local';
- transfer.isRetirement = true;
- transfer.toCompanyMeta = req.toCompanyMeta;
- transfer.retirementType = req.type;
- // await this.programmeTransferRepo.save(transfer);
-
- if (requester.companyId != toCompany.companyId) {
- transfer.status = TransferStatus.PENDING;
- await this.emailService.sendEmail(
- toCompany.email,
- EmailTemplates.RETIRE_REQUEST,
- {
- "name": fromCompany.name,
- "requestedCompany": requestedCompany.name,
- "credits": transfer.creditAmount,
- "serialNo": programme.serialNo,
- "programmeName": programme.title
- });
- } else {
- transfer.status = TransferStatus.PROCESSING;
- autoApproveTransferList.push(transfer);
- }
- allTransferList.push(transfer);
- }
- const results = await this.programmeTransferRepo.insert(allTransferList)
- console.log(results)
- for (const i in allTransferList) {
- allTransferList[i].requestId = results.identifiers[i].requestId;
- }
-
- let updateProgramme = undefined;
- for (const trf of autoApproveTransferList) {
- this.logger.log(`Retire auto approve received ${trf}`)
- updateProgramme = (await this.doTransfer(trf, this.getUserRef(requester), req.comment, true)).data;
- }
- if (updateProgramme) {
- return new DataResponseDto(HttpStatus.OK, updateProgramme)
- }
- return new DataListResponseDto(allTransferList, allTransferList.length)
+ let updateProgramme = undefined;
+ for (const trf of autoApproveTransferList) {
+ this.logger.log(`Retire auto approve received ${trf}`);
+ updateProgramme = (
+ await this.doTransfer(
+ trf,
+ `${this.getUserRef(requester)}#${toCompany.companyId}#${
+ toCompany.name
+ }#${fromCompanyMap[trf.fromCompanyId].companyId}#${
+ fromCompanyMap[trf.fromCompanyId].name
+ }`,
+ req.comment,
+ true
+ )
+ ).data;
+ }
+ if (updateProgramme) {
+ return new DataResponseDto(HttpStatus.OK, updateProgramme);
+ }
+ return new DataListResponseDto(allTransferList, allTransferList.length);
+ }
+
+ async issueProgrammeCredit(req: ProgrammeIssue, user: User) {
+ this.logger.log(
+ `Programme ${req.programmeId} approve. Comment: ${req.comment}`
+ );
+ const program = await this.programmeLedger.getProgrammeById(
+ req.programmeId
+ );
+ if (!program) {
+ throw new HttpException(
+ "Programme does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
- async issueProgrammeCredit(req: ProgrammeIssue, user: User) {
- this.logger.log(`Programme ${req.programmeId} approve. Comment: ${req.comment}`)
- const program = await this.programmeLedger.getProgrammeById(req.programmeId);
- if (!program) {
- throw new HttpException("Programme does not exist", HttpStatus.BAD_REQUEST);
- }
+ if (program.currentStage != ProgrammeStage.AUTHORISED) {
+ throw new HttpException(
+ "Programme is not in authorised state",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ if (program.creditEst - program.creditIssued < req.issueAmount) {
+ throw new HttpException(
+ "Programme issue credit amount can not exceed pending credit amount",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ const updated: any = await this.programmeLedger.issueProgrammeStatus(
+ req.programmeId,
+ this.configService.get("systemCountry"),
+ program.companyId,
+ req.issueAmount,
+ this.getUserRefWithRemarks(user, req.comment)
+ );
+ if (!updated) {
+ return new BasicResponseDto(
+ HttpStatus.BAD_REQUEST,
+ `Does not found a pending programme for the given programme id ${req.programmeId}`
+ );
+ }
- if (program.currentStage != ProgrammeStage.AUTHORISED) {
- throw new HttpException("Programme is not in authorised state", HttpStatus.BAD_REQUEST);
- }
- if (program.creditEst - program.creditIssued < req.issueAmount) {
- throw new HttpException("Programme issue credit amount can not exceed pending credit amount", HttpStatus.BAD_REQUEST);
- }
- const updated: any = await this.programmeLedger.issueProgrammeStatus(req.programmeId, this.configService.get('systemCountry'), program.companyId, req.issueAmount, this.getUserRef(user))
- if (!updated) {
- return new BasicResponseDto(HttpStatus.BAD_REQUEST, `Does not found a pending programme for the given programme id ${req.programmeId}`)
- }
+ updated.company = await this.companyRepo.find({
+ where: { companyId: In(updated.companyId) },
+ });
+ if (updated.certifierId && updated.certifierId.length > 0) {
+ updated.certifier = await this.companyRepo.find({
+ where: { companyId: In(updated.certifierId) },
+ });
+ }
- updated.company = await this.companyRepo.find({
- where: { companyId: In(updated.companyId) },
- })
- if (updated.certifierId && updated.certifierId.length > 0) {
- updated.certifier = await this.companyRepo.find({
- where: { companyId: In(updated.certifierId) },
- })
- }
- return new DataResponseDto(HttpStatus.OK, updated)
+ const hostAddress = this.configService.get("host");
+ updated.company.forEach(async (company) => {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ company.companyId,
+ EmailTemplates.CREDIT_ISSUANCE,
+ {
+ programmeName: updated.title,
+ credits: updated.creditIssued,
+ serialNumber: updated.serialNo,
+ pageLink:
+ hostAddress + `/programmeManagement/view?id=${updated.programmeId}`,
+ }
+ );
+ });
+
+ return new DataResponseDto(HttpStatus.OK, updated);
+ }
+
+ async approveProgramme(req: ProgrammeApprove, user: User) {
+ this.logger.log(
+ `Programme ${req.programmeId} approve. Comment: ${req.comment}`
+ );
+ const program = await this.programmeLedger.getProgrammeById(
+ req.programmeId
+ );
+ if (!program) {
+ throw new HttpException(
+ "Programme does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
- async approveProgramme(req: ProgrammeApprove, user: User) {
- this.logger.log(`Programme ${req.programmeId} approve. Comment: ${req.comment}`)
- const program = await this.programmeLedger.getProgrammeById(req.programmeId);
- if (!program) {
- throw new HttpException("Programme does not exist", HttpStatus.BAD_REQUEST);
- }
+ if (program.currentStage != ProgrammeStage.AWAITING_AUTHORIZATION) {
+ throw new HttpException(
+ "Programme is not in pending state",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ if (program.creditEst < req.issueAmount) {
+ throw new HttpException(
+ "Programme issue credit amount can not exceed estimated credit amount",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+ const updated: any = await this.programmeLedger.authProgrammeStatus(
+ req.programmeId,
+ this.configService.get("systemCountry"),
+ program.companyId,
+ req.issueAmount,
+ this.getUserRefWithRemarks(user, req.comment)
+ );
+ if (!updated) {
+ return new BasicResponseDto(
+ HttpStatus.BAD_REQUEST,
+ `Does not found a pending programme for the given programme id ${req.programmeId}`
+ );
+ }
- if (program.currentStage != ProgrammeStage.AWAITING_AUTHORIZATION) {
- throw new HttpException("Programme is not in pending state", HttpStatus.BAD_REQUEST);
- }
- if (program.creditEst < req.issueAmount) {
- throw new HttpException("Programme issue credit amount can not exceed estimated credit amount", HttpStatus.BAD_REQUEST);
- }
- const updated: any = await this.programmeLedger.authProgrammeStatus(req.programmeId, this.configService.get('systemCountry'), program.companyId, req.issueAmount, this.getUserRef(user))
- if (!updated) {
- return new BasicResponseDto(HttpStatus.BAD_REQUEST, `Does not found a pending programme for the given programme id ${req.programmeId}`)
- }
+ updated.company = await this.companyRepo.find({
+ where: { companyId: In(updated.companyId) },
+ });
+ if (updated.certifierId && updated.certifierId.length > 0) {
+ updated.certifier = await this.companyRepo.find({
+ where: { companyId: In(updated.certifierId) },
+ });
+ }
- updated.company = await this.companyRepo.find({
- where: { companyId: In(updated.companyId) },
- })
- if (updated.certifierId && updated.certifierId.length > 0) {
- updated.certifier = await this.companyRepo.find({
- where: { companyId: In(updated.certifierId) },
- })
- }
- return new DataResponseDto(HttpStatus.OK, updated)
+ const hostAddress = this.configService.get("host");
+ updated.company.forEach(async (company) => {
+ await this.emailHelperService.sendEmailToOrganisationAdmins(
+ company.companyId,
+ EmailTemplates.PROGRAMME_AUTHORISATION,
+ {
+ programmeName: updated.title,
+ authorisedDate: new Date(updated.txTime),
+ serialNumber: updated.serialNo,
+ programmePageLink:
+ hostAddress + `/programmeManagement/view?id=${updated.programmeId}`,
+ }
+ );
+ });
+
+ return new DataResponseDto(HttpStatus.OK, updated);
+ }
+
+ async rejectProgramme(req: ProgrammeReject, user: User) {
+ this.logger.log(
+ `Programme ${req.programmeId} reject. Comment: ${req.comment}`
+ );
+
+ const updated = await this.programmeLedger.updateProgrammeStatus(
+ req.programmeId,
+ ProgrammeStage.REJECTED,
+ ProgrammeStage.AWAITING_AUTHORIZATION,
+ this.getUserRefWithRemarks(user, req.comment)
+ );
+ if (!updated) {
+ throw new HttpException(
+ "Programme does not exist",
+ HttpStatus.BAD_REQUEST
+ );
}
- async rejectProgramme(req: ProgrammeReject, user: User) {
- this.logger.log(`Programme ${req.programmeId} reject. Comment: ${req.comment}`)
+ await this.emailHelperService.sendEmailToProgrammeOwnerAdmins(
+ req.programmeId,
+ EmailTemplates.PROGRAMME_REJECTION,
+ { reason: req.comment }
+ );
- const updated = await this.programmeLedger.updateProgrammeStatus(req.programmeId, ProgrammeStage.REJECTED, ProgrammeStage.AWAITING_AUTHORIZATION, this.getUserRef(user))
- if (!updated) {
- throw new HttpException("Programme does not exist", HttpStatus.BAD_REQUEST);
- }
- return new BasicResponseDto(HttpStatus.OK, "Successfully updated")
+ return new BasicResponseDto(HttpStatus.OK, "Successfully updated");
+ }
+
+ private getUserName = async (usrId: string) => {
+ this.logger.debug(`Getting user [${usrId}]`);
+ if (usrId == "undefined" || usrId == "null") {
+ return null;
}
+ const userId = Number(usrId);
+ if (userId == undefined || userId == null) {
+ return null;
+ }
+ if (this.userNameCache[userId]) {
+ this.logger.debug(
+ `Getting user - cached ${userId} ${this.userNameCache[userId]}`
+ );
+ return this.userNameCache[userId];
+ }
+ const user = await this.userService.findById(Number(userId));
+ this.logger.debug(`Getting user - user ${user}`);
+ if (user) {
+ this.logger.debug(`Getting user - user ${user.name}`);
+ this.userNameCache[userId] = user.name;
+ return user.name;
+ }
+ return null;
+ };
- private getUserRef = (user: any) => {
- return `${user.companyId}#${user.companyName}#${user.id}#${user.name}`;
+ private getCompanyIdAndUserIdFromRef = (ref: string) => {
+ if (!ref) {
+ return null;
+ }
+ const parts = ref.split("#");
+ if (parts.length > 2) {
+ return {
+ id: parts[2],
+ companyId: Number(parts[0]),
+ };
}
-}
\ No newline at end of file
+ if (parts.length > 0) {
+ return {
+ companyId: Number(parts[0]),
+ };
+ }
+ return null;
+ };
+
+ private getUserRef = (user: any) => {
+ return `${user.companyId}#${user.companyName}#${user.id}`;
+ };
+
+ private getUserRefWithRemarks = (user: any, remarks: string) => {
+ return `${user.companyId}#${user.companyName}#${user.id}#${remarks}`;
+ };
+}
diff --git a/lambda/services/src/shared/server.ts b/lambda/services/src/shared/server.ts
index a7fb85609..9352b1221 100644
--- a/lambda/services/src/shared/server.ts
+++ b/lambda/services/src/shared/server.ts
@@ -33,12 +33,12 @@ function setupSwagger(nestApp: INestApplication, name: string, httpBase: String)
.setVersion('0.0.1')
.addBearerAuth()
.addApiKey()
- .addServer(`/${process.env.NODE_ENV}`)
+ .addServer(`${process.env.NODE_ENV === 'local' ? '/local': '/'}`)
.build();
-
+ // ${process.env.NODE_ENV}
const document = SwaggerModule.createDocument(nestApp, config);
- SwaggerModule.setup(`${httpBase}/docs`, nestApp, document, {
+ SwaggerModule.setup(`${httpBase}`, nestApp, document, {
customSiteTitle: 'API Documentation',
swaggerOptions: {
docExpansion: 'none',
diff --git a/lambda/services/src/shared/user/user.module.ts b/lambda/services/src/shared/user/user.module.ts
index ca6cbbcbc..5a08d4aa3 100644
--- a/lambda/services/src/shared/user/user.module.ts
+++ b/lambda/services/src/shared/user/user.module.ts
@@ -1,4 +1,4 @@
-import { Logger, Module } from '@nestjs/common';
+import { forwardRef, Logger, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../../shared/entities/user.entity';
import { UserService } from './user.service';
@@ -9,6 +9,7 @@ import configuration from '../../shared/configuration';
import { ConfigModule } from '@nestjs/config';
import { CompanyModule } from '../company/company.module';
import { UtilModule } from '../util/util.module';
+import { EmailHelperModule } from '../email-helper/email-helper.module';
@Module({
imports: [
@@ -24,7 +25,8 @@ import { UtilModule } from '../util/util.module';
TypeOrmModule.forFeature([User]),
CaslModule,
EmailModule,
- CompanyModule,
+ forwardRef(() => CompanyModule),
+ forwardRef(() => EmailHelperModule),
UtilModule
],
providers: [UserService, Logger],
diff --git a/lambda/services/src/shared/user/user.service.ts b/lambda/services/src/shared/user/user.service.ts
index 2a91db8b2..740447141 100644
--- a/lambda/services/src/shared/user/user.service.ts
+++ b/lambda/services/src/shared/user/user.service.ts
@@ -1,6 +1,8 @@
import {
+ forwardRef,
HttpException,
HttpStatus,
+ Inject,
Injectable,
Logger,
UseFilters,
@@ -48,6 +50,7 @@ export class UserService {
private configService: ConfigService,
private helperService: HelperService,
@InjectEntityManager() private entityManger: EntityManager,
+ @Inject(forwardRef(() => CompanyService))
private companyService: CompanyService,
private counterService: CounterService
) {}
@@ -201,6 +204,14 @@ export class UserService {
return err;
});
if (result.affected > 0) {
+ await this.emailService.sendEmail(
+ user.email,
+ EmailTemplates.CHANGE_PASSOWRD,
+ {
+ name: user.name,
+ countryName: this.configService.get("systemCountryName"),
+ }
+ );
return new BasicResponseDto(HttpStatus.OK, "Successfully updated");
}
throw new HttpException(
@@ -257,6 +268,7 @@ export class UserService {
HttpStatus.INTERNAL_SERVER_ERROR
);
}
+
async create(
userDto: UserDto,
companyId: number,
@@ -343,6 +355,8 @@ export class UserService {
u.apiKey = await this.generateApiKey(userDto.email);
}
+ const hostAddress = this.configService.get("host");
+
if (company) {
company.companyId = parseInt(
await this.counterService.incrementCount(CounterType.COMPANY, 3)
@@ -359,13 +373,25 @@ export class UserService {
HttpStatus.INTERNAL_SERVER_ERROR
);
}
+
+ if(company.email){
+ await this.emailService.sendEmail(company.email, EmailTemplates.ORGANISATION_CREATE, {
+ organisationName: company.name,
+ countryName: this.configService.get("systemCountryName"),
+ organisationRole: company.companyRole,
+ home: hostAddress,
+ });
+ }
}
- await this.emailService.sendEmail(u.email, EmailTemplates.REGISTER_EMAIL, {
+ await this.emailService.sendEmail(u.email, EmailTemplates.USER_CREATE, {
name: u.name,
- countryName: this.configService.get("systemCountry"),
- password: u.password,
- apiKeyText: u.apiKey ? `
Api Key: ${u.apiKey}` : "",
+ countryName: this.configService.get("systemCountryName"),
+ tempPassword: u.password,
+ home: hostAddress,
+ email: u.email,
+ liveChat: this.configService.get("liveChat"),
+ helpDoc: this.configService.get("helpDocumentation"),
});
u.createdTime = new Date().getTime();
@@ -412,6 +438,7 @@ export class UserService {
});
const { apiKey, password, ...resp } = usr;
+
return resp;
}
@@ -494,4 +521,28 @@ export class UserService {
HttpStatus.INTERNAL_SERVER_ERROR
);
}
+
+ async getGovAdminAndManagerUsers() {
+ const result = await this.userRepo
+ .createQueryBuilder("user")
+ .where("user.role in (:admin, :manager)",{admin:Role.Admin, manager:Role.Manager})
+ .andWhere("user.companyRole= :companyRole",{companyRole:CompanyRole.GOVERNMENT})
+ .select(['user.name','user.email'])
+ .getRawMany();
+
+ return result;
+
+ //return result.map((item) => {return item.user_email});
+ }
+
+ async getOrganisationAdminAndManagerUsers(organisationId) {
+ const result = await this.userRepo
+ .createQueryBuilder("user")
+ .where("user.role in (:admin,:manager)",{admin:Role.Admin, manager:Role.Manager})
+ .andWhere("user.companyId= :companyId",{companyId:organisationId})
+ .select(['user.name','user.email'])
+ .getRawMany();
+
+ return result;
+ }
}
diff --git a/lambda/services/src/shared/util/country.service.ts b/lambda/services/src/shared/util/country.service.ts
index 5a448e510..6675eea84 100644
--- a/lambda/services/src/shared/util/country.service.ts
+++ b/lambda/services/src/shared/util/country.service.ts
@@ -21,6 +21,12 @@ export class CountryService {
})) != null;
}
+ async getCountryName(alpha2: string) {
+ return (await this.countryRepo.findOneBy({
+ alpha2: alpha2
+ }))?.name;
+ }
+
async getCountryList(query: QueryDto) {
const resp = await this.countryRepo
diff --git a/lambda/services/src/shared/util/helpers.service.ts b/lambda/services/src/shared/util/helpers.service.ts
index 18d67624a..550564c07 100644
--- a/lambda/services/src/shared/util/helpers.service.ts
+++ b/lambda/services/src/shared/util/helpers.service.ts
@@ -27,11 +27,31 @@ export class HelperService {
return value;
}
- private isNotLower(key: string) {
- if (["txType", "typeOfMitigation", "currentStage", "sectoralScope", "companyRole", "state", "status"].includes(key))
+ private prepareKey(col: string, table?: string) {
+ let key;
+ if (col.includes('->>')) {
+ const parts = col.split('->>');
+ key = `"${parts[0]}"->>'${parts[1]}'`
+ } else {
+ key = `"${col}"`
+ }
+ return `${table ? table + "." : ""}${key}`
+ }
+
+ private isLower(key: string) {
+ if (["email", "name", "companyName", "taxId", "country", "title", "externalId", "serialNo", "programmeTitle"].includes(key))
return true;
}
+ public generateSortCol(col: string) {
+ if (col.includes('->>')) {
+ const parts = col.split('->>');
+ return `"${parts[0]}"->>'${parts[1]}'`
+ } else {
+ return `"${col}"`
+ }
+ }
+
public generateWhereSQLChartStastics(
data: chartStatsRequestDto,
extraSQL: string,
@@ -212,17 +232,17 @@ export class HelperService {
if (this.isQueryDto(e.value)) {
return `(${this.prepareValue(e.value, table)})`;
} else if (e.operation === 'ANY') {
- return `${this.prepareValue(e.value, table)} = ANY(${table ? table + "." : ""}"${e.key}")`;
+ return `${this.prepareValue(e.value, table)} = ANY(${this.prepareKey(e.key, table)})`;
} else if (e.keyOperation) {
- return `${e.keyOperation}(${table ? table + "." : ""}"${e.key}") ${
+ return `${e.keyOperation}(${this.prepareKey(e.key, table)}) ${
e.operation
} ${this.prepareValue(e.value, table, true)}`;
- } else if (!this.isNotLower(e.key) && typeof e.value === "string") {
- return `LOWER(${table ? table + "." : ""}"${e.key}") ${
+ } else if (this.isLower(e.key) && typeof e.value === "string") {
+ return `LOWER(${this.prepareKey(e.key, table)}) ${
e.operation
} ${this.prepareValue(e.value, table, true)}`;
} else {
- return `${table ? table + "." : ""}"${e.key}" ${
+ return `${this.prepareKey(e.key, table)} ${
e.operation
} ${this.prepareValue(e.value, table)}`;
}
@@ -235,15 +255,15 @@ export class HelperService {
if (this.isQueryDto(e.value)) {
return `(${this.prepareValue(e.value, table)})`;
} else if (e.operation === 'ANY') {
- return `${this.prepareValue(e.value, table)} = ANY(${table ? table + "." : ""}"${e.key}")`;
+ return `${this.prepareValue(e.value, table)} = ANY(${this.prepareKey(e.key, table)})`;
} else if (e.keyOperation) {
- return `${e.keyOperation}(${table ? table + "." : ""}"${e.key}") ${
+ return `${e.keyOperation}(${this.prepareKey(e.key, table)}) ${
e.operation
} ${this.prepareValue(e.value, table, true)}`;
- } else if (!this.isNotLower(e.key) && typeof e.value === "string") {
- return `LOWER(${table ? table + "." : ""}"${e.key}") ${e.operation} ${this.prepareValue(e.value, table, true)}`;
+ } else if (this.isLower(e.key) && typeof e.value === "string") {
+ return `LOWER(${this.prepareKey(e.key, table)}) ${e.operation} ${this.prepareValue(e.value, table, true)}`;
} else {
- return `${table ? table + "." : ""}"${e.key}" ${e.operation} ${this.prepareValue(e.value, table)}`;
+ return `${this.prepareKey(e.key, table)} ${e.operation} ${this.prepareValue(e.value, table)}`;
}
})
.join(" or ");
diff --git a/web/.env-cmdrc b/web/.env-cmdrc
index 865b6471f..15ae73e47 100644
--- a/web/.env-cmdrc
+++ b/web/.env-cmdrc
@@ -1,5 +1,5 @@
{
"development": {
- "REACT_APP_BACKEND": "https://ck5kt5uaw1.execute-api.us-east-1.amazonaws.com/dev/api"
+ "REACT_APP_BACKEND": "https://ck5kt5uaw1.execute-api.us-east-1.amazonaws.com/dev"
}
}
diff --git a/web/package.json b/web/package.json
index ca7c68d7b..5ef7e8657 100644
--- a/web/package.json
+++ b/web/package.json
@@ -8,9 +8,6 @@
"@casl/react": "^3.1.0",
"@craco/craco": "^7.0.0",
"@mapbox/mapbox-sdk": "^0.14.0",
- "@testing-library/jest-dom": "^5.16.5",
- "@testing-library/react": "^13.4.0",
- "@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.3",
"@types/react": "^18.0.25",
@@ -19,7 +16,6 @@
"antd": "^4.24.1",
"apexcharts": "^3.36.3",
"axios": "^1.1.3",
- "bcrypt": "^5.1.0",
"bootstrap-icons": "^1.10.2",
"buffer": "^6.0.3",
"class-transformer": "^0.5.1",
diff --git a/web/public/Assets/i18n/companyProfile/en.json b/web/public/Assets/i18n/companyProfile/en.json
index bb83d9191..acb7f25dd 100644
--- a/web/public/Assets/i18n/companyProfile/en.json
+++ b/web/public/Assets/i18n/companyProfile/en.json
@@ -2,6 +2,7 @@
"title": "Organisation Details",
"subTitle": "View the details of the selected organisation",
"deauthorise": "DEACTIVATE",
+ "reActivate": "REACTIVATE",
"organisationDetailsHeading": "Organisation Details",
"name" : "Name",
"taxId" : "Tax ID",
@@ -17,5 +18,8 @@
"activeStatus": "Active",
"deauthorisedStatus": "Deactivated",
"remarks": "Remarks",
- "deauthorisationSuccess": "Company deactivated successfully!"
+ "deauthorisationSuccess": "Company deactivated successfully!",
+ "reActivateConfirmHeaderText": "Are you sure you want to reactivate this organisation?",
+ "reActivateConfirmText": "Note: all users associated with the organisation will also be reactivated.",
+ "reactivationSuccess": "Company reactivated successfully!"
}
\ No newline at end of file
diff --git a/web/public/Assets/i18n/dashboard/en.json b/web/public/Assets/i18n/dashboard/en.json
index e69de29bb..18de9ae71 100644
--- a/web/public/Assets/i18n/dashboard/en.json
+++ b/web/public/Assets/i18n/dashboard/en.json
@@ -0,0 +1,71 @@
+{
+ "today": "Today",
+ "last7": "Last 7 days",
+ "last14": "Last 14 days",
+ "overall": "OVERALL",
+ "mine": "MINE",
+ "auth": "Authorised",
+ "rejected": "Rejected",
+ "pending": "Pending",
+ "programmesPending": "Programmes Pending",
+ "trasnferReqReceived": "Pending Transfers Received",
+ "trasnferReqInit": "Pending Transfers Sent",
+ "programmesUnCertified": "Programmes Certifiable",
+ "programmesCertified": "Programmes Certified",
+ "creditBal": "Credit Balance",
+ "creditCertified": "Credit Certified",
+ "programmes": "Programmes",
+ "credits": "Credits",
+ "certifiedCredits": "Certified Credits",
+ "totalProgrammes": "Total Programmes",
+ "totalProgrammesSector": "Total Programmes: Sector",
+ "totalCredits": "Total Credits",
+ "totalCreditsCertified": "Total Credits Certified",
+ "programmeLocations": "Programme Locations",
+ "trasnferLocations": "Transfer Locations International",
+ "tTprogrammespendingGoverment": "Pending state programmes awaiting authorisation",
+ "tTTransferReqSentGovernment": "Pending credit transfer requests sent to programme owners initiated by your organisation",
+ "tTCreditBalanceGovernment": "Total credit balance owned by your organisation",
+ "tTProgrammesGoverment": "Number of programmes created during the specified period and their programme state in the carbon registry at present",
+ "tTCreditsGovernment": "Number of credits of authorised programmes created during the specified period and their credit state in the carbon registry at present",
+ "tTCertifiedCreditsGovernment": "Number of credits of programmes created during the specified period, uncertified, certified and revoked in the carbon registry at present",
+ "tTTotalProgrammesGovernment": "Graphical representation of the number of programmes created during the specified period in each programme state in the carbon registry at present",
+ "tTTotalProgrammesSectorGovernment": "Graphical representation of the number of programmes in each programme sector created during the specified time in the carbon registry",
+ "tTTotalCreditsGovernment": "Graphical representation of the number of credits of programmes created during the specified period in each credit state in the carbon registry at present",
+ "tTTotalCreditsCertifiedGovernment": "Graphical representation of the number of credits of programmes created during the specified period certified, uncertified and revoked in the carbon registry at present",
+ "tTProgrammeLocationsGovernment": " Locations of the programmes created during the specified period and their programme states in the carbon registry at present",
+ "tTTransferLocationsGovernment": "Locations of credits of international transfer requests recognised during the specified period",
+ "tTTransferReqRecProgrammeDev": "Pending credit transfer requests received by your organisation",
+ "tTTransferReqInitProgrammeDev": "Pending local credit transfer requests initiated by your organisation",
+ "tTCreditBalanceProgrammeDev": "Total credit balance owned by your organisation",
+ "tTProgrammesProgrammeDev": "Number of programmes created during the specified period and their programme state in the carbon registry at present, owned by your organisation",
+ "tTCreditsProgrammeDev": "Number of credits of authorised programmes created during the specified period and their credit state in the carbon registry at present, owned by your organisation",
+ "tTCertifiedCreditsProgrammeDev": "Number of credits of programmes created during the specified period, uncertified, certified and revoked in the carbon registry at present, owned by your organisation",
+ "tTTotalProgrammesProgrammeDev": "Graphical representation of the number of programmes created during the specified period, owned by your organisation, in each programme state in the carbon registry at present",
+ "tTTotalProgrammesSecProgrammeDev": "Graphical representation of the number of programmes owned by your organisation, in each programme sector created during the specified time in the carbon registry",
+ "tTTotalCreditsProgrammeDev": "Graphical representation of the number of credits of programmes created during the specified period, owned by your organisation, in each credit state in the carbon registry at present",
+ "tTTotalCertifiedCreditsProgrammeDev": "Graphical representation of the number of credits of programmes created during the specified period, owned by your organisation, certified, uncertified and revoked in the carbon registry at present",
+ "tTProgrammeLocationsProgrammeDev": "Locations of the programmes created during the specified period, owned by your organisation, and their programme states in the carbon registry at present",
+ "tTTrasnferLocationsProgrammeDev": "Locations of credits international transfer requests of programmes owned by your organisation recognised during the specified period",
+ "tTProgrammesUnCertiCertifier": "Number of programmes not yet certified including certificates revoked by your organisation",
+ "tTProgrammesCertiCertifier": "Number of programmes certified by your organisation",
+ "tTCreditCertifiedCertifier": "Number of credits certified by your organisation",
+ "tTProgrammesCertifierMine": "Number of programmes created during the specified period, certified by your organisation, and their programme state in the carbon registry at present",
+ "tTCreditsCertifierMine": "Number of credits of authorised programmes created during the specified period, certified by your organisation and their credit state in the carbon registry at present",
+ "tTCertifiedCreditsCertifierMine": "Number of credits of programmes created during the specified period, certified by your organisation, uncertified, certified and revoked in the carbon registry at present",
+ "tTTotalProgrammesCertifierMine": "Graphical representation of the number of programmes in each programme sector created during the specified time, certified by your company, in the carbon registry",
+ "tTTotalProgrammesSecCertifierMine": "Graphical representation of the number of programmes in each programme sector created during the specified time, certified by your company, in the carbon registry",
+ "tTTotalCreditsCertifierMine": "Graphical representation of the number of credits of programmes created during the specified period, certified by your organisation, in each credit state in the carbon registry at present",
+ "tTTotalCertifiedCreditsCertifierMine": "Graphical representation of the number of credits of programmes certified, uncertified and revoked by your organisation, spread over the specified time",
+ "tTProgrammeLocationsCertifierMine": "Locations of the programmes created during the specified period, certified by your organisation, and their programme states in the carbon registry at present",
+ "tTTrasnferLocationsCertifierMine": "Locations of credits of international transfer requests of programmes certified by your organisation recognised during the specified period",
+ "tTProgrammesCertifierOverall": "Number of programmes created during the specified period and their programme state in the carbon registry at present",
+ "tTCreditsCertifierOverall": "Number of credits of authorised programmes created during the specified period and their credit state in the carbon registry at present",
+ "tTCertifiedCreditsCertifierOverall": "Number of credits of programmes created during the specified period, uncertified, certified and revoked in the carbon registry at present",
+ "tTTotalProgrammesCertifierOverall": "Graphical representation of the number of programmes created during the specified period in each programme state in the carbon registry at present",
+ "tTTotalProgrammesSecCertifierOverall": "Graphical representation of the number of programmes in each programme sector created during the specified time in the carbon registry",
+ "tTTotalCreditsCertifierOverall": "Graphical representation of the number of credits of programmes created during the specified period in each credit state in the carbon registry at present",
+ "tTTotalCertifiedCreditsCertifierOverall": "Graphical representation of the number of credits of programmes created during the specified period certified, uncertified and revoked in the carbon registry at present",
+ "tTProgrammeLocationsCertifierOverall": "Locations of the programmes created during the specified period and their programme states in the carbon registry at present",
+ "tTTrasnferLocationsCertifierOverall": "Locations of credits of international transfer requests recognised during the specified period"
+}
\ No newline at end of file
diff --git a/web/public/Assets/i18n/view/en.json b/web/public/Assets/i18n/view/en.json
index 8dd15e0bf..0c9f62541 100644
--- a/web/public/Assets/i18n/view/en.json
+++ b/web/public/Assets/i18n/view/en.json
@@ -73,6 +73,39 @@
"company": "Company",
"accept": "Accept",
"certifier": "Certifier",
- "seeMine": "Mine"
-
+ "seeMine": "Mine",
+ "tlRejectTitle": "Transfer Request Rejected",
+ "tlTxRejectDesc": "The request to transfer {} {} credits from {} to {} was rejected by {}",
+ "tlInitTitle": "Transfer Initiated",
+ "tlInitDesc": "{} {} credits of this programme were requested to be transferred from {} to {} by {}",
+ "via": "via",
+ "tlDescInit": "credits of this programme were requested to be transferred from",
+ "tlCreate": "Programme Created",
+ "tlCreateDesc": "The programme was created with a valuation of {} {} credits.",
+ "tlAuth": "Authorised",
+ "tlAuthDesc": "The programme was authorised for {} {} credits until {} with the Serial Number {} by {}",
+ "tlIssue": "Credits Issued",
+ "tlIssueDesc": "The programme was issued {} {} credits by {}",
+ "tlReject": "Rejected",
+ "tlRejectDesc": "The programme was rejected by {}",
+ "tlTransfer": "Transferred",
+ "tlTransferDesc": "{} {} credits of this programme were transferred from {} to {} by {}",
+ "tlRevoke": "Certificate Revoked",
+ "tlRevokeDesc": "The certification of this programme was revoked {} by {}",
+ "tlCertify": "Certified",
+ "tlCertifyDesc": "The programme was certified by {}",
+ "tlRetire": "Credits Retired",
+ "tlRetireDesc": "{} {} credits of this programme were retired from {} {}as {} by {}",
+ "tlFrozen": "Credits Frozen",
+ "tlFrozenDesc": "{} {} credits were frozen due to the deactivation of {} by {}",
+ "tlRetInit": "Retirement Initiated",
+ "tlRetInitDesc": "{} {} credits of this programme were requested to be transferred from {} {}as {} by {}",
+ "tlRetRejectTitle": "Retirement Not Recognised",
+ "tlTxRetRejectDesc": "The request to transfer {} {} credits from {} to {} was not recognised by {}",
+ "tlRetCancelTitle": "Retire request cancelled",
+ "tlTxCancelTitle": "Transfer Request Cancelled",
+ "tlTxCancelDesc": "The request to transfer {} {} credits from {} to {} was cancelled by {}",
+ "tlTxCancelSystemDesc": "The request to transfer {} {} credits from {} to {} was cancelled by the system due to the deactivation of {} by the government",
+ "tlUnFrozen": "Credits Unfrozen",
+ "tlUnFrozenDesc": "{} frozen {} credits of the programme have been unfrozen due to the reactivation of {} by {}"
}
\ No newline at end of file
diff --git a/web/public/index.html b/web/public/index.html
index d2ef87cf5..690ad9451 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -25,7 +25,8 @@
Carbon Credit Management
-
+
Carbon Credit Registry - Antarctic Region
@@ -34,8 +35,7 @@
-
+
@@ -45,8 +45,7 @@
-
+
@@ -69,7 +68,13 @@
data-tf-tooltip="Hey 👋 How can I help you? " data-tf-chat data-tf-medium="snippet"
data-tf-hidden="utm_source=xxxxx,utm_medium=xxxxx,utm_campaign=xxxxx,utm_term=xxxxx,utm_content=xxxxx"
style="all:unset;">
-
+