All files / src/cronjobs due-actions-notification.cronjob.ts

44.12% Statements 15/34
0% Branches 0/18
33.33% Functions 1/3
40.63% Lines 13/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1001x 1x 1x 1x 1x 1x 1x 1x     1x 1x     1x       1x   1x                                                                                                                                                              
import { AwsServiceBindings, AwsSesService } from '@logchain/components/aws';
import { Constants, Module } from '@logchain/configs';
import { Status } from '@logchain/models';
import { FlowActionRepository } from '@logchain/repositories';
import { inject } from '@loopback/core';
import { CronJob, cronJob } from '@loopback/cron';
import { AnyObject, PositionalParameters, repository } from '@loopback/repository';
import moment from 'moment';
 
@cronJob()
export class DueActionsNotificationCronJob extends CronJob {
  private readonly lock_key = '1701060650';
 
  @inject(AwsServiceBindings.SES_SERVICE)
  public ses_service: AwsSesService;
  
  constructor(
    @repository(FlowActionRepository)
    public flowActionRepository: FlowActionRepository,
  ) {
    super({
      name: 'Due Actions Notification',
      onTick: async () => {
        await this.performCronJob();
      },
      cronTime: '0 * * * *', // Run hourly but send on 00:00 to 01:00 at company timezone
      start: true,
    });
  }
 
  async performCronJob() {
    const isUnLocked = await this.flowActionRepository.execute(`SELECT pg_try_advisory_lock(${this.lock_key});`);
    if (!isUnLocked[0].pg_try_advisory_lock) {
      return;
    }
 
    await this.flowActionRepository.execute(`SELECT pg_advisory_unlock(${this.lock_key});`);
    const now = moment();
    const startAt = now.toDate();
    console.log(`*** Cronjob: ${this.name} started at ${startAt}`);
    // Your code start here
    const sql = `
    SELECT 
      u.name,
      u.contact_email,
      u.language,
      co.name as company_name,
      current_timestamp AT TIME ZONE co.timezone as expected_date,
      jsonb_agg(
        jsonb_build_object(
          'flow_id', fa.flow_id, 
          'flow_name', flows."name",
          'action_type', CASE WHEN adv.document_name = 'documents' THEN 'Document' ELSE 'Form' END,
          'action_item', CASE WHEN adv.document_name = 'documents' THEN d."name" ELSE fo."name" END,
          'expected_date', fjp.expected_date AT TIME ZONE co.timezone
        ) ORDER BY fa.flow_id DESC
      ) as actions
    FROM flows_action fa
    JOIN flows_journey_point fjp ON fjp.id = fa.flow_journey_point_id
    LEFT JOIN actions_data_view adv ON fa.id = adv.flow_action_id
    JOIN flows ON flows.id = fa.flow_id AND flows.status = 6
    JOIN companies co ON co.id = fa.company_id
    JOIN users_view u ON u.company_id = co.id AND u.status = 2
    JOIN role_rights rr ON rr.role_id = u.role_id
    LEFT JOIN documents d ON (d.id = adv.id AND adv.document_name = 'documents')
    LEFT JOIN forms fo ON fo.id = fa.form_id
    WHERE fjp.expected_date IS NOT NULL AND fa.company_id IS NOT NULL AND co.timezone IS NOT NULL AND fjp.container_order IS NOT NULL
    AND fjp.expected_date AT TIME ZONE co.timezone < current_timestamp AT TIME ZONE co.timezone
    AND EXTRACT(HOUR FROM current_timestamp AT TIME ZONE co.timezone) >= 0 AND EXTRACT(HOUR FROM current_timestamp AT TIME ZONE co.timezone) < 1
    AND (rr.retrieve = $1 AND rr.feature_id = $2)
    AND (u.settings::jsonb->>'received_due_action')::BOOLEAN = TRUE
    AND fa.status = $3
    GROUP BY u.name, u.contact_email, u.language, co.name, co.timezone
    `;
    const params: AnyObject | PositionalParameters = [1, Module.PhysicalTransport.JourneyMgmt, Status.FlowAction.New];
    const records = await this.flowActionRepository.execute(sql, params) as AnyObject[];
    const recepients = [];
    if (records?.length) {
      for(const record of records) {
        recepients.push({
          email: record.contact_email,
          language: record.language,
          replacement_data: {
            username: record.name,
            company_name: record.company_name,
            expected_date: record.expected_date,
            list_actions_text: this.ses_service.generateTemplateActionsText(record.actions),
            list_actions_html: this.ses_service.generateTemplateActionsHTML(record.actions),
            show_limit_message: record?.actions?.length > Constants.MaxItemsForActionListInEmail ? true : false
          }
        });
      }
      await this.ses_service.sendBulkTemplatedEmail(Constants.Email.InformUserProcessDueActionData, recepients);
    }
    // --------------------
    const finishedAt = new Date();
    console.log(`*** Cronjob: ${this.name} finished at ${finishedAt}. Total in ${(finishedAt.getTime() - startAt.getTime()) / 1000}ms`);
    await this.flowActionRepository.execute(`SELECT pg_advisory_unlock(${this.lock_key});`);
  }
}