All files / src/repositories flow.repository.ts

60.71% Statements 34/56
0% Branches 0/27
14.29% Functions 1/7
59.26% Lines 32/54

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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 3161x 1x                   1x 1x                       1x   1x 1x   1x                         128x   128x   128x   128x   128x   128x   128x   128x   128x     128x 128x       128x 128x       128x       128x       128x       128x 128x       128x 128x       128x 128x       128x 128x                                                                                                                                                                                                                                                                                                                                                                                                                                                
import {Getter, inject} from '@loopback/core';
import {BelongsToAccessor, HasManyRepositoryFactory, HasOneRepositoryFactory, Options, repository} from '@loopback/repository';
import {
  CompanyRepository,
  FlowDefaultRepository,
  FlowGroupRepository,
  FlowJourneyPointRepository,
  FlowPersonaRepository,
  TradeLaneRepository,
  UserViewRepository,
} from '.';
import {LogChainDataSource} from '../datasources';
import {
  Company,
  Flow,
  FlowDefault,
  FlowDiscussion,
  FlowGroup,
  FlowJourneyPoint,
  FlowPersona,
  FlowRelations,
  TradeLane,
  User,
} from '../models';
import {BaseCrudRepository} from './base-crud.repository.base';
import {FlowDiscussionRepository} from './flow-discussion.repository';
import {DateUtils} from '@logchain/utils';
import { Constants } from '@logchain/configs';
 
export class FlowRepository extends BaseCrudRepository<Flow, typeof Flow.prototype.id, FlowRelations> {
  public readonly flow_default: HasOneRepositoryFactory<FlowDefault, typeof Flow.prototype.id>;
  public readonly trade_lane: HasOneRepositoryFactory<TradeLane, typeof Flow.prototype.id>;
  public readonly creator: BelongsToAccessor<User, typeof User.prototype.id>;
  public readonly company: BelongsToAccessor<Company, typeof Company.prototype.id>;
  public readonly modifier: BelongsToAccessor<User, typeof User.prototype.id>;
  public readonly groups: HasManyRepositoryFactory<FlowGroup, typeof Flow.prototype.id>;
  public readonly journey_points: HasManyRepositoryFactory<FlowJourneyPoint, typeof Flow.prototype.id>;
  public readonly personas: HasManyRepositoryFactory<FlowPersona, typeof Flow.prototype.id>;
  public readonly histories: HasManyRepositoryFactory<FlowDiscussion, typeof Flow.prototype.id>;
  constructor(
    @inject('datasources.logchain') dataSource: LogChainDataSource,
    @repository.getter('FlowGroupRepository')
    protected flow_group_repo_getter: Getter<FlowGroupRepository>,
    @repository.getter('FlowPersonaRepository')
    protected flow_persona_repo_getter: Getter<FlowPersonaRepository>,
    @repository.getter('FlowJourneyPointRepository')
    protected flow_journey_point_repo_getter: Getter<FlowJourneyPointRepository>,
    @repository.getter('TradeLaneRepository')
    protected trade_lane_repo_getter: Getter<TradeLaneRepository>,
    @repository.getter('UserViewRepository')
    protected user_repo_getter: Getter<UserViewRepository>,
    @repository.getter('CompanyRepository')
    protected company_repo_getter: Getter<CompanyRepository>,
    @repository.getter('FlowDefaultRepository')
    protected flow_default_repo_getter: Getter<FlowDefaultRepository>,
    @repository.getter('FlowDiscussionRepository')
    protected flow_discussion_repo_getter: Getter<FlowDiscussionRepository>,
  ) {
    super(Flow, dataSource);
 
    // ------- FLOW DEFAULT ------- //
    this.flow_default = this.createHasOneRepositoryFactoryFor('flow_default', flow_default_repo_getter);
    this.registerInclusionResolver('flow_default', this.flow_default.inclusionResolver);
    // ------- FLOW DEFAULT ------- //
 
    // ------- TRADE LANE   ------- //
    this.trade_lane = this.createHasOneRepositoryFactoryFor('trade_lane', trade_lane_repo_getter);
    this.registerInclusionResolver('trade_lane', this.trade_lane.inclusionResolver);
    // ------- TRADE LANE   ------- //
 
    // ------- CREATOR      ------- //
    this.creator = this.createBelongsToAccessor('creator', user_repo_getter);
    // ------- CREATOR      ------- //
 
    // ------- COMPANY CREATOR      ------- //
    this.company = this.createBelongsToAccessor('company', company_repo_getter);
    // ------- COMPANY CREATOR      ------- //
 
    // ------- MODIFIER     ------- //
    this.modifier = this.createBelongsToAccessor('modifier', user_repo_getter);
    // ------- MODIFIER     ------- //
 
    // ------- GROUPS     ------- //
    this.groups = this.createHasManyRepositoryFactoryFor('groups', flow_group_repo_getter);
    this.registerInclusionResolver('groups', this.groups.inclusionResolver);
    // ------- GROUPS     ------- //
 
    // ------- JOURNEY POINTS     ------- //
    this.journey_points = this.createHasManyRepositoryFactoryFor('journey_points', flow_journey_point_repo_getter);
    this.registerInclusionResolver('journey_points', this.journey_points.inclusionResolver);
    // ------- JOURNEY POINTS     ------- //
 
    // ------- REFERENCE PERSONAS  ------- //
    this.personas = this.createHasManyRepositoryFactoryFor('personas', flow_persona_repo_getter);
    this.registerInclusionResolver('personas', this.personas.inclusionResolver);
    // ------- REFERENCE PERSONAS  ------- //
 
    // -------- FLOW DISCUSSION ------- //
    this.histories = this.createHasManyRepositoryFactoryFor('histories', flow_discussion_repo_getter);
    this.registerInclusionResolver('histories', this.histories.inclusionResolver);
  }
 
  /**
   * Validate expected_date.
   * @param flow_id.
   */
  async validateExpectedDate(flow_id: number, step: FlowJourneyPoint, expected_date: string | Date,): Promise<boolean> {
    if (typeof(expected_date) !== 'string') {
      expected_date = expected_date.toISOString();
    }
    const result = await this.execute(
      `SELECT expected_date FROM flows_journey_point WHERE flow_id = $1 AND id <> $2 AND "order" <> 0 AND "order" < $3 AND container_order = $4 ORDER BY "expected_date" DESC LIMIT 1;`,
      [flow_id, step.id, step.order, step.container_order],
    );
    return result?.length && result[0].expected_date ? DateUtils.isSameOrAfter(expected_date, result[0].expected_date) : true;
  }
 
  /**
   * Validate trucking must have on access list of step.
   * @param flow_id.
   */
  async mustHaveTruckingOnAccess(step_id: number): Promise<boolean> {
    if (step_id) {
      const result = await this.execute(
        `SELECT must_have_trucking_on_access(flows_journey_point) AS valid FROM flows_journey_point WHERE id = $1;`,
        [step_id],
      );
 
      return result?.length && result[0].valid;
    }
    
    return false;
  }
 
  /**
   * Validate companies of flow must have on access list of step.
   * @param flow_id.
   */
  async allCompaniesMustHaveAccessToStep(flow_id: number): Promise<boolean> {
    if (flow_id) {
      const result = await this.execute(
        `SELECT 
          COUNT(DISTINCT flow_persona_id_from_group) = COUNT(DISTINCT fp1.id) AS valid
        FROM 
          flows_persona fp1
        INNER JOIN
        (
          SELECT DISTINCT
            fg.flow_id,
            UNNEST(flow_personas) flow_persona_id_from_group
          FROM flows_group fg
          WHERE fg.flow_id = $1 AND fg.order <> 0
        ) AS fp2 ON fp2.flow_id = fp1.flow_id
        WHERE fp2.flow_id = $1
        GROUP BY fp2.flow_id;`,
        [flow_id],
      );
 
      return result?.length && result[0].valid;
    }
    
    return false;
  }
 
  /**
   * Removes duplicated steps for a specific flow with no location.
   *
   * @param {number} flow_id - The ID of the flow.
   * @return {Promise<boolean>} A promise that resolves to true if the removal was successful, otherwise false.
   */
  async removeDuplicatedForNoLocationSteps(flow_id: number): Promise<boolean> {
    try {
      await this.execute(
        `DELETE FROM flows_journey_point WHERE flow_id = $1 AND "order" <> 0 AND persona_id = 0 AND container_order <> 1;`,
        [flow_id],
      );
 
      await this.execute(
        `UPDATE flows_journey_point SET container_order = NULL WHERE flow_id = $1 AND "order" <> 0 AND persona_id = 0 AND container_order = 1;`,
        [flow_id],
      );
  
      return true;
    } catch (error) {
      console.error(error);
      return false;
    }
  }
 
  /**
   * Get all flows that have consignment_number and last modified after 3 hours using SQL query to flow validation
   *
   * @return {Promise<Flow[]>} The promise of Flow[]
   */
  async getPendingXMLFlows(): Promise<Flow[]> {
    // Get all flows that have consignment_number and last modified after 3 hours using SQL query to flow validation
    const sql = `
    SELECT id, company_id, created_by
    FROM flows f
    WHERE f.consignment_num IS NOT NULL
    AND NOW() >=  f.modified_date + INTERVAL '${process.env.XML_PENDING_HOURS} hours'
    AND f.status = 0
    AND f.live_status = 0`;
 
    return await this.execute(sql) as Array<Flow>;
  }
 
  /**
   * Precomputes the master report with the provided options.
   *
   * @param {Options} options - The options for precomputing the report.
   * @return {Promise<void>} A Promise that resolves when the report precomputation is completed.
   */
  async precomputeReportMaster(options: Options = {}): Promise<void> {
    const query = `INSERT INTO report_precomputation (
                report_type,
                flow_id, 
                flow_name, 
                flow_reference_1, 
                flow_reference_2, 
                flow_reference_3,
                company_id, 
                job_no, 
                container_no, 
                pol_code, 
                pol_shipping, 
                pol_booking, 
                pol_voyage, 
                pol_vessel, 
                pol_etd, 
                trans_code, 
                trans_eta, 
                trans_time, 
                trans_etd, 
                trans_shipping, 
                trans_booking, 
                trans_voyage, 
                trans_vessel, 
                consignee, 
                pod_code, 
                pod_eta, 
                flow_status_id, 
                flow_status_name, 
                final_transit_time, 
                comments, 
                date_created, 
                date_updated
              )
              SELECT DISTINCT ON (report_type, company_id, flow_id, container_no)
                $1::int4 AS report_type,
                report_master_view.flow_id, 
                report_master_view.flow_name, 
                report_master_view.flow_reference_1, 
                report_master_view.flow_reference_2, 
                report_master_view.flow_reference_3,
                report_master_view.company_id,
                report_master_view.job_no, 
                report_master_view.container_no, 
                report_master_view.pol_code, 
                report_master_view.pol_shipping, 
                report_master_view.pol_booking, 
                report_master_view.pol_voyage, 
                report_master_view.pol_vessel, 
                report_master_view.pol_etd, 
                report_master_view.trans_code, 
                report_master_view.trans_eta, 
                report_master_view.trans_time, 
                report_master_view.trans_etd, 
                report_master_view.trans_shipping, 
                report_master_view.trans_booking, 
                report_master_view.trans_voyage, 
                report_master_view.trans_vessel, 
                report_master_view.consignee, 
                report_master_view.pod_code, 
                report_master_view.pod_eta, 
                report_master_view.flow_status_id, 
                report_master_view.flow_status_name, 
                report_master_view.final_transit_time, 
                report_master_view.comments, 
                now() AS date_created, 
                now() AS date_updated
              FROM report_master_view
              ON CONFLICT (report_type, company_id, flow_id, container_no) DO UPDATE 
              SET date_updated = NOW(),
                flow_name = EXCLUDED.flow_name,
                flow_reference_1 = EXCLUDED.flow_reference_1,
                flow_reference_2 = EXCLUDED.flow_reference_2,
                flow_reference_3 = EXCLUDED.flow_reference_3,
                job_no = EXCLUDED.job_no,
                container_no = EXCLUDED.container_no,
                pol_code = EXCLUDED.pol_code,
                pol_shipping = EXCLUDED.pol_shipping,
                pol_booking = EXCLUDED.pol_booking,
                pol_voyage = EXCLUDED.pol_voyage,
                pol_vessel = EXCLUDED.pol_vessel,
                pol_etd = EXCLUDED.pol_etd,
                trans_code = EXCLUDED.trans_code,
                trans_eta = EXCLUDED.trans_eta,
                trans_time = EXCLUDED.trans_time,
                trans_etd = EXCLUDED.trans_etd,
                trans_shipping = EXCLUDED.trans_shipping,
                trans_booking = EXCLUDED.trans_booking,
                trans_voyage = EXCLUDED.trans_voyage,
                trans_vessel = EXCLUDED.trans_vessel,
                consignee = EXCLUDED.consignee,
                pod_code = EXCLUDED.pod_code,
                pod_eta = EXCLUDED.pod_eta,
                flow_status_id = EXCLUDED.flow_status_id,
                flow_status_name = EXCLUDED.flow_status_name,
                final_transit_time = EXCLUDED.final_transit_time,
                comments = EXCLUDED.comments;
              `;
    await this.execute(query, [ Constants.ReportType.MasterReport ]);
  }
}