Untitled

 avatar
unknown
plain_text
2 years ago
40 kB
4
Indexable
const Datastore = require('nedb-promises')
// const moment = require('moment')
const _ = require('lodash')
const md5 = require('md5')
const loki = require('lokijs')

const fs = require('fs')
const path = require('path')

const stringSimilarity = require('string-similarity')

const cliProgress = require('cli-progress')

const workProcCat = _.keyBy(require('./workProcCat').map(wpc => ({
    ...wpc,
    cleanDesc: wpc.Description.toLowerCase().replaceAll(/[^a-z]+/g, '')
})), 'cleanDesc')

const roleTypes = _.keyBy(require('./mwcRoleType'), 'RoleTypeName')

const roleConvert = {
    '2nd Off': 11000010,
    'Admin Off': '',
    '2nd Eng': 11000008,
    'Chief Off': 90000009,
    'Cargo Eng': '',
    'Master': 90000007,


    'Chief Eng': 90000008,
    'Elect': 11000003,
    '3rd Eng': 11000009,
    'Cook Stwd': '',
    '3rd Off': 11000011,
    '4th Eng': '',
}

function clean (s) {
    return s.toLowerCase().replaceAll(/[^a-z0-9]/g, '')
}

function convertGenericName (name) {
    return name
        ? name
        .replace(/^((no\.|no|ae|me|number|num)\.*\s*\d+[abc]?)/gi, ' ')
        .replace(/\s+(no\.|no|ae|me|number|num|m)\d+$/gi, ' ')
        .replace(/^(PRT|STBD|STB|PORT|AFT|FWD|Starboard)\s+/gi, ' ')
        .replace(/(no\.|no|ae|me|number|num)\s+\d+\s+(PRT|STBD|STB|PORT|AFT|FWD|Starboard)/gi, ' ')
        .replace(/\s+\d+\s+(PRT|STBD|STB|PORT|AFT|FWD)/gi, ' ')
        .replace(/[\s\-]+\(?((no\.|no|ae|me|number|num)\.*\s*\d+[abc]?\)?)/gi, ' ')
        .replace(/\s+(PRT|STBD|STB|PORT|AFT|FWD|Starboard)(\s+|$)/gi, ' ')
        .replace(/\s+\((PRT|STBD|STB|PORT|AFT|FWD|Starboard|P|C|S|Stbd Aft|Stbd Fwd|Port Aft|Port Fwd)\)/gi, ' ')
        .replace(/\s+\((inner|outer)\)/gi, ' ')
        .replace(/\s+(P|C|S)$/gi, ' ')
        .replace(/\s+'?(A|B|C)'?\s+/g, ' ')
        .replace(/\s+\d+$/gi, '')
        .replace(/\s+/g, ' ')
        .replace(/^\-/g, '')
        .trim()
        : '';
}

const uom = require('./uom')

const {
    logger,
    InvalidReferenceError,
} = require('./log')

const Entities = require('./entities')

async function main () {
    const f = fs.readdirSync(path.resolve(__dirname, '..'))
    for (let file of f) {
        if (file.endsWith('.log') || file.startsWith('injection.')) fs.unlinkSync(file)
    }

    const dryRun = false

    let START_ID = 805000000
    const targetDB = 'BGCUAT'
    // const targetDB = 'BGCUAT'
    
    const targetDBReplace = 'BGCUAT'
    // const targetDBReplace = 'BGCUAT'

    const tt = require('./target_tables')(targetDB)
    const target = require('knex')({
        client: 'mssql',
        log: { error: m => logger.error(m) },
        connection: {
            // password: 'Aa@123456',
            password: 'mspwstr0@ng',

            server: "127.0.0.1",
            // server: '20.231.30.144',

            // user: 'sa',
            user: 'donald2',

            database: targetDB,
            port: 1433,
            options: {
                encrypt: false,
                trustServerCertificate: true,
            },
            requestTimeout: 60000,
        }
    });
    const ent = new Entities(target, tt)

    let queryLogFile = path.resolve(__dirname, '..', 'injection.sql')
    let logLines = 0
    let closingStreams = []
    let queryLogStream = fs.createWriteStream(queryLogFile)

    const restartQueryLog = (name) => {
        closingStreams.push(new Promise(r => queryLogStream.end(r)))
        queryLogFile = path.resolve(__dirname, '..', 'injection.'+name+'.sql')
        queryLogStream = fs.createWriteStream(queryLogFile)
        logLines = 0
    }

    const logQuery = (q) => {
        if (q.startsWith('select')) return;
        q = q.replace(`[${targetDB}]`, `[${targetDBReplace}]`)
        if (q.startsWith('insert')) {
            let v = q.indexOf(' values ')
            v = v+' values '.length
            queryLogStream.write(q.substring(0, v))
            queryLogStream.write(`\n`)
            q = q.substring(v).replaceAll('), (', `),\n(`)
            q = q.replaceAll(/([(\s]{1})(false)([,)]{1})/g, '$10$3')
            q = q.replaceAll(/([(\s]{1})(true)([,)]{1})/g, '$11$3')
            let m = q.split(/\r\n|\r|\n/).length
            logLines = logLines + m
        }
        queryLogStream.write(q)
        queryLogStream.write(`\n`)
    }

    target.on('start', builder => {
        let q = builder.toString()
        q = q.replace(';select @@rowcount', ';')
        logQuery(q)
        // queryLogger.info(q)
        return builder
    })

    logger.info('Target Cleanup Start')
    for (let t of Object.values(tt)) {

        if (t.indexOf('[swFleetSystem]') !== -1) continue;
        if (t.indexOf('[mwcRoleType]') !== -1) continue;

        if (t.indexOf('[mwrChangeLogColumn]') !== -1) {
          await target.from(t)
            .where('mwrChangeLogKey', '>=', 2064)
            .delete()

            continue
        };
        
        if (t.indexOf('[mwrChangeLog]') !== -1) {
            await target.from(t)
            .where('ChangeDateTime', '>=', '2022-06-01 00:00:45.663')
            .delete()

            continue
        };

        await target.from(t)
        .where('ID', '>=', START_ID)
        .delete()
    }
    logger.info('Target Cleanup Finish')

    const dbSources = [
        'AMADI',
        'AMALI',
        'AMANI',
        'ARKAT',
    ]

    const multibar = new cliProgress.MultiBar({
        clearOnComplete: false,
        hideCursor: true
    }, cliProgress.Presets.shades_grey);


    let mwcSitesID = START_ID
    let swWorkProcCatID = START_ID
    let swWorkProcFleetID = START_ID

    async function batchStore(data, table, batchSize = null) {
        if (data.length === 0) return
        const fields = Object.keys(data[0]).length
        if (!batchSize) batchSize = Math.min(Math.floor(2000 / fields), 900)
        const bar1 = multibar.create(Math.ceil(data.length / batchSize), 0)
        let batches = Math.ceil(data.length / batchSize)
        try {
            for (let i = 0; i < batches; i++) {
                const c = data.splice(0, batchSize)
                let q = target(table).insert(c)
                if (dryRun) {
                    logQuery(q.toString())
                } else {
                    await q
                }
                bar1.increment()
            }
        } catch (e) {
            logger.error(e.message)
            throw e
        } finally {
            bar1.stop()
            multibar.remove(bar1)
        }
    }

    const swCompanyArticleInserts = []

    function getOrCreateWorkProcCat(Description) {
        if (!Description) return null
        const cleanDesc = Description.toLowerCase().replaceAll(/[^a-z]+/g, '')
        if (workProcCat[cleanDesc]) return workProcCat[cleanDesc].ID
        const ID = ++swWorkProcCatID
        swWorkProcCatInserts.push({
            ID,
            Description: Description.substring(0, 20),
        })
        workProcCat[cleanDesc] = { ID }
        return ID
    }

    async function * iterate (source, table, order = null, fields = undefined, perPage = 100) {
        let query
        if (typeof table === 'string') {
            query = source.from(table)
        } else {
            query = table
            table = query._single.table
        }

        const [{ '': count }] = await source(table).count()
        if (count === 0) return

        let bar
        if (multibar) bar = multibar.create(count, 0, null, {
            format: `[{bar}] {percentage}% | {value}/{total} [${table}]`
        })

        const pages = Math.ceil(count / perPage) - 1
        for (let i = 0; i <= pages; i++) {
            let q = query.clone().select(fields).limit(perPage).offset(i * perPage)
            if (order) q.orderBy(...(Array.isArray(order) ? order : [order]))
            const data = await q
            for (let d of data) {
                yield d
                if (bar) bar.increment()
            }
        }
        if (bar) bar.stop();
    }

    const sites = {}
    // Create sites
    restartQueryLog('1.sites')
    for (let db of dbSources) {
        logger.defaultMeta.db = db
        const source = require('knex')({
            client: 'mssql',
            connection: {
                // password: 'HqHZfKNjkks7',
                // server: 'amos-sql.makini.io',
                // password: 'Aa@123456',
                password: 'mspwstr0@ng',
                
                server: "127.0.0.1",
                // server: 'localhost',
                // server: '20.231.30.144',

                // user: 'sa',
                user: 'donald2',

                database: db,
                port: 1433,
                options: {
                    encrypt: false,
                    trustServerCertificate: true,
                },
                requestTimeout: 60000,
            }
        });

        const mwcSitesKey = ++mwcSitesID
        await target(tt.mwcSites).insert({
            ID: mwcSitesKey,
            SiteID: db,
            SiteName: db,
        })

        const site = ent.getSite(mwcSitesKey)
        sites[db] = {
            site,
            mwcSitesKey,
            source,
        }
    }

    const swWorkProcFleetInserts = []
    const swWorkProcCatInserts = []
    const swWorkProcFleetTaskInserts = []
    let swWorkProcFleetTaskID = START_ID
    let hashes = []
    let pmHashes = {}
    let pmMap = {}
    let pmSchedule = {}
    const swWorkProcFleet = ent.db.addCollection('swWorkProcFleet', {
        unique: ['ID'],
        indices: ['JobCode', 'ComponentCode']
    })//new Datastore({ inMemoryOnly: true });
    restartQueryLog('2.wp')

    function getIntervals(WSType, Frequency) {
        switch (WSType.toLowerCase()) {
            case 'h':
                return {
                    IntervalRH: Frequency,
                }
            case 'm':
                return {
                    IntervalCalUnit: 'M',
                    IntervalCal: Frequency
                }
            default:
                InvalidReferenceError.log('Invalid WSType', { WSType }, { tblPMWorkSheet: PMWorksheetID })
                return {}
        }
    }

    function getRole(IssueTo) {
        if (roleTypes[IssueTo]) return roleTypes[IssueTo].ID
        else if (roleConvert[IssueTo]) return roleConvert[IssueTo]
        else return 90000008
    }

    for (let db of dbSources) {
        logger.defaultMeta.db = db
        const {
            // site,
            source,
        } = sites[db]

        const q = source.from({
            pm: 'tblPMWorkSheet'
        }).leftJoin({
            tj: 'tblJob',
        }, 'pm.JobCode', '=', 'tj.JobCode')
        .orderBy('Active', 'DESC')
        .orderBy('SafetyNotes')

        for await (let {
            PMWorksheetID,
            JobCode,
            WSType,
            Frequency,
            FullJobText,
            SafetyNotes,
            ShipSafetyNotes,
            BriefDescription,
            IssueTo,
            ShortDescription,
            ComponentCode,
        } of iterate(source, q, null, [
            'pm.PMWorksheetID',
            'pm.ComponentCode',
            'pm.JobCode',
            'pm.ShortDescription',
            'pm.WSType',
            'pm.Frequency',
            'pm.FullJobText',
            'pm.SafetyNotes',
            'pm.ShipSafetyNotes',
            'pm.IssueTo',
            'tj.BriefDescription'
        ], 500)) {
            let stdTasks = [
                SafetyNotes ? await ent.createStdTask(SafetyNotes) : null,
                ShipSafetyNotes ? await ent.createStdTask(ShipSafetyNotes) : null
            ].filter(s => !!s)

            FullJobText = FullJobText.trim()
            ShortDescription = convertGenericName(ShortDescription.trim())

            const c = clean(ShortDescription)
            const hash = md5(WSType+Frequency+c)

            const {
                IntervalCal = null,
                IntervalRH = null,
                IntervalCalUnit = null,
            } = getIntervals(WSType, Frequency)
            if (!IntervalCal && !IntervalRH) continue;

            const mwcRoleTypeKey = getRole(IssueTo)

            let i = (typeof pmHashes[hash] !== 'undefined') ? pmHashes[hash] : -1

            if (i !== -1) {
                let same = swWorkProcFleetInserts[i]
                if (same.Description.length < FullJobText) swWorkProcFleetInserts[i].Description = FullJobText
                pmMap[PMWorksheetID] = same.ID
                pmSchedule[PMWorksheetID] = [
                    same.IntervalRH || '-',
                    same.IntervalCal || '-',
                    same.IntervalCalUnit || '-',
                    same.swWorkProcCategoryKey || '-',
                ].join('')
                continue
            }

            let WpID = (swWorkProcFleet.find({ JobCode, ComponentCode }).length+1).toString().padStart(4, '0')
            const ID = ++swWorkProcFleetID
            const swWorkProcCategoryKey = getOrCreateWorkProcCat(BriefDescription)
            const Title = ShortDescription.substring(0, 75)
            const wp = {
                ID,
                Title,
                RescheduleType: IntervalRH ? 2 : 1,
                IntervalRH,
                // swDepartmentKey,
                Description: convertGenericName(FullJobText),
                IntervalCalUnit,
                IntervalCal,
                ScheduleBasis: 'C',
                IsEqptOverhaul: FullJobText.substring(0, 200).toLowerCase().indexOf('overhaul') !== -1,
                mwcRoleTypeKey,
                // EstHours,
                // GracePeriodCal,
                swWorkProcCategoryKey,
                swWorkProcFleetTypeKey: 1,
                WpID: `${JobCode}-${WpID}`,
                // IsCritical,
                ComponentCode,
                JobCode,
            }
            pmHashes[hash] = (swWorkProcFleetInserts.push(wp) - 1)

            await swWorkProcFleet.insert({
                ...wp,
                stdTasks,
                ComponentCode,
                JobCode,
            })

            pmMap[PMWorksheetID] = ID
            pmSchedule[PMWorksheetID] = [
                IntervalRH || '-',
                IntervalCal || '-',
                IntervalCalUnit || '-',
                swWorkProcCategoryKey || '-',
            ].join('')
            swWorkProcFleetTaskInserts.push(...stdTasks.map((smStdTaskKey, i) => ({
                ID: ++swWorkProcFleetTaskID,
                smStdTaskKey,
                swWorkProcFleetKey: ID,
                DisplaySequence: i+1,
            })))
        }
    }
    await batchStore(swWorkProcCatInserts, 'swWorkProcCategory')
    await batchStore(swWorkProcFleetInserts.map(w => _.omit(w, 'ComponentCode', 'JobCode')), 'swWorkProcFleet')
    delete hashes
    delete pmHashes

    restartQueryLog('3.global')
    for (let db of dbSources) {
        logger.defaultMeta.db = db

        const {
            source,
            site,
        } = sites[db]

        {
            let swMfgInserts = []
            for await (let { Manufacturer, ManufacturerID } of iterate(source, 'tblManufacturer', 'ManufacturerID')) {
                if (!Manufacturer) continue;
                const m = ent.createManufacturer({ Manufacturer })
                site.addMfgRef({ ID: m.ID,ManufacturerID })
                if (!m.$loki) swMfgInserts.push(m)
            }
            await batchStore(swMfgInserts, tt.swMfg)
            delete swMfgInserts
        }

        {
            let swRHMeterInserts = []
            for await (let m of iterate(source, 'tblAssembly', 'AssemblyCode')) {
                m = site.createMeter(m)
                if (m) swRHMeterInserts.push(m)
            }
            await batchStore(swRHMeterInserts, tt.swRHMeter)
            delete swRHMeterInserts
        }

        {
            let swSparesStoreInserts = []
            for await (let m of iterate(source, 'tblLocation', 'LocationID')) {
                m = site.createSparesStore(m)
                if (m) swSparesStoreInserts.push(m)
            }
            await batchStore(swSparesStoreInserts, tt.swSparesStore)
            delete swSparesStoreInserts
        }

        {
            const swRHMeterReadingInserts = []
            for await (let m of iterate(source, 'tblAssemblyLog', 'AssemblyCode')) {
                m = site.createMeterReading(m)
                if (m) swRHMeterReadingInserts.push(m)
            }
            await batchStore(swRHMeterReadingInserts, tt.swRHMeterReading)
        }
    }

    await ent.populateFS()

    restartQueryLog('4.eq')
    for (let db of dbSources) {
        logger.defaultMeta.db = db
        const {
            site,
            source,
        } = sites[db]

        const select = [
            'ComponentDescription',
            'AssemblyCode',
            'ManufacturerID',
            'Active',
            'ComponentCode',
            'Type',
            'CriticalComponent',
            'SerialNumber',
            'CMSNumber',
            'TotalRunningHours',
            'Comments',
            'InstructionBookNumber',
        ]

        const allJobs = _.groupBy(await source.from('tblPMWorkSheet').select([
            'ComponentCode',
            'PMWorksheetID',
        ]), 'ComponentCode')

        const swEquipmentInserts = []

        for await (let c of iterate(
            source,
            'tblComponent',
            ['InstructionBookNumber', 'DESC'],
            select
        )) {
            if (!c.ComponentDescription) {
                InvalidReferenceError.log('Empty component name', { tblComponent: c.Reference })
                continue;
            }

            const jobs = (allJobs[c.ComponentCode] || []).map(j => pmSchedule[j.PMWorksheetID]).sort().join(',')

            const e = await site.createEquipment({
                ...c,
                jobs,
            })
            if (e) swEquipmentInserts.push(e)
        }

        await batchStore(swEquipmentInserts, tt.swEquipment, null, 4)
    }


    let swSparesID = START_ID
    let swSparesMfgID = START_ID
    let swSparesLocationsID = START_ID
    let swCompanyEqptCoArtID = START_ID
    let swCompanyEqptCoArtInserts = []
    // const swSpares = ent.db.addCollection('swSpares', {
    //     indices: ['swCompanyArticleKey', 'PartNumber', 'ComponentCode', 'swMfgKey', 'mwcSitesKey']
    // })

    const updateDateExpire = []
    restartQueryLog('5.spares')

    for (let db of dbSources) {
        logger.defaultMeta.db = db
        const {
            site,
            source,
            mwcSitesKey,
        } = sites[db]

        const swSparesInserts = []

        const swSparesMfgInserts = []
        const swSparesLocationsInserts = []

        const clearSpares = async () => {
            await batchStore(swCompanyArticleInserts, tt.swCompanyArticle)
            await batchStore(swSparesInserts, tt.swSpares)
            await batchStore(swSparesMfgInserts, tt.swSparesMfg)
            await batchStore(swSparesLocationsInserts, tt.swSparesLocations)
        }

        let sqp = source.from({ p: 'tblPart' })
        .select([
            'p.FullDescription',
            'p.ComponentCode',
            'p.UnitOfMeasure',
            'p.ManufacturersPartNumber',
            'p.InstructionBookReference',
            'p.LocationID',
            'p.PartNumber',
            'p.RemainingOnBoard',
            'p.QtyAllocated',
            'p.MinHoldingLevel',
            'p.Price',
            'pe.ExpiryDate',
        ])
        // .innerJoin({ c: 'tblComponent' }, 'c.ComponentCode', 'p.ComponentCode')
        .leftJoin({ pe: 'tblPartExpiry' }, function () {
            this.on('pe.ComponentCode', '=', 'p.ComponentCode').andOn(
                'pe.PartNumber',
                '=',
                'p.PartNumber'
            )
        })
        .whereNot('p.FullDescription', 'like', '%see no.%')
        for await (let p of iterate(source, sqp, 'ComponentCode', null, 500)) {
            let {
                FullDescription: Description,
                ComponentCode,
                UnitOfMeasure,
                ManufacturersPartNumber: MfgPartNo,
                InstructionBookReference: DrawingNumber,
                LocationID,
                PartNumber,

                RemainingOnBoard: QtyOnHand,
                QtyAllocated: QtyInUse,
                MinHoldingLevel: MinLevel,
                Price: AvgPrice,
                ExpiryDate: DateExpire,
            } = p

            // if (!ComponentCode) continue;
            if (!Description) continue;
            if (Description.toLowerCase().indexOf('see no.') !== -1) continue;
            const comp = site.swEquipment.by('ComponentCode', ComponentCode)
            if (!comp) {
                InvalidReferenceError.log('Invalid component code', { ComponentCode }, { tblPart: PartNumber })
                continue;
            }
            UnitOfMeasure = UnitOfMeasure.trim().toUpperCase()

            const swUnitMeasKey = uom[UnitOfMeasure]
            if (!swUnitMeasKey) {
                InvalidReferenceError.log('Invalid UOM', { UnitOfMeasure }, { tblPart: PartNumber })
                continue;
            }

            const PartName = Description.split(/(\r|\n)+/)[0]

            let {
                MfgName,
                MfgModel,
                swMfgKey,
                swCompanyEqptKey,
            } = comp

            const data = {
                PartName,
                Description,
                MfgPartNo,
                DrawingNumber,
                MfgName,
                MfgModel,
                swMfgKey,
                swUnitMeasKey,
            }

            const { data: article, exists } = ent.getOrCreateCompanyArticle(data, swCompanyEqptKey)
            const swCompanyArticleKey = article.ID
            let ID = ++swSparesID

            const e = site.swSpares.find({ swCompanyArticleKey })
            if (e.length === 0) {
                site.swSpares.insert({
                    ID,
                    PartNumber,
                    ComponentCode,
                    swMfgKey,
                    swCompanyArticleKey,
                    swCompanyEqptKey,
                })

                swSparesInserts.push({
                    ID,
                    ...data,
                    mwcSitesKey,
                    QtyOnHand,
                    QtyInUse,
                    MinLevel,
                    AvgPrice,
                    swUnitMeasKey,
                    swCompanyArticleKey,
                    DateExpire,
                })


                // tblPartExpiry.swSparekey /Exiredate =  swSpares.Exiredate

                // tblMaintenanceCompleted.PMWorksheedId = 0  => unschedule

                if (DateExpire) {
                  updateDateExpire.push({
                    ID,
                    DateExpire,
                  });
                }
            } else {
                ID = _.head(e).ID
            }

            if (exists) {
                // const existing = await swSpares.findOne({ swCompanyArticleKey })
                // ID = existing.ID
            } else {
                swCompanyArticleInserts.push(article)

                if (swMfgKey) swSparesMfgInserts.push({
                    ID: ++swSparesMfgID,
                    swSparesKey: ID,
                    swMfgKey,
                    mwcSitesKey,
                    MfgPartNo,
                })

                swCompanyEqptCoArtInserts.push({
                    ID: ++swCompanyEqptCoArtID,
                    swCompanyArtKey: swCompanyArticleKey,
                    swCompanyEqptKey,
                    PartID: PartNumber,
                    DrawingNo: DrawingNumber,
                })
            }

            if (LocationID) {
                const loc = site.swSparesStore.by('LocationID', LocationID)
                if (!loc) {
                } else {
                    swSparesLocationsInserts.push({
                        ID: ++swSparesLocationsID,
                        swSparesKey: ID,
                        swSparesStoreKey: loc.ID,
                        mwcSitesKey,
                        QtyOnHandLocation: QtyOnHand,
                    })
                }
            }
            if (swSparesInserts.length >= 5000) await clearSpares()
        }
        await clearSpares()

    }
    swCompanyEqptCoArtInserts = _.uniq(swCompanyEqptCoArtInserts)
    await batchStore(swCompanyEqptCoArtInserts, tt.swCompanyEqptCoArt)

    // 8.updateDateExpire
    // DATE: April 17 2023 - Donald - Datafac
    // ISSUE: swSpares.DateExpire is missing
    // SOLUTION: Create SQL query then run SQL query in database
    const buildDateExpireSQL = (ID, DateExpire) => {
        const q = target(tt.swSpares).where('ID', '=', ID).update({ DateExpire })
        logQuery(q.toString().replace(";select @@rowcount", ";"))
    }

    _.map(updateDateExpire, ({ ID, DateExpire }) => buildDateExpireSQL(ID, DateExpire))
    // END 8.updateDateExpire

    let swCoEqptWPID = START_ID
    let swWorkProcID = START_ID
    let swWpStdTaskID = START_ID
    let swSparesAdjustID = START_ID
    let swEqptHistoryID = START_ID
    const swCoEqptWPInserts = []
    let i = 0
    let swCompEqptWPStored = []

    let swWorkProcIDinserted = {}

    for (let db of dbSources) {
        logger.defaultMeta.db = db
        restartQueryLog('6.work.'+(++i))
        const {
            site,
            source,
            mwcSitesKey,
        } = sites[db]
        const swWorkProc = site.db.addCollection('swWorkProc', {
            unique: ['PMWorksheetID'],
        })

        const swWorkProcInserts = []
        const swWpStdTaskInserts = []
        const swEqptWorkProcInserts = []

        const q = source.from('tblPMWorkSheet')
        .select([
            'PMWorksheetID',
            'ComponentCode',
            'ShortDescription',
            'FullJobText',
            'JobCode',
            'ClassSocietySurveyCode',
            'DateLastDone',
            'HoursDone',
            'Active',
        ])
        .orderBy('Active', 'DESC')
        .orderBy('PMWorksheetID')
        for await (let pm of iterate(source, q, null)) {
            let {
                PMWorksheetID,
                ComponentCode,
                ShortDescription: Title,
                FullJobText: Description,
                JobCode: ProcedureID,
                ClassSocietySurveyCode: RegulationID,
                DateLastDone: LastCompleted,
                HoursDone: LastCompletedRH,
                Active,
            } = pm

            const comp = site.swEquipment.by('ComponentCode', ComponentCode)
            if (!comp || !comp.ID) {
                InvalidReferenceError.log('Invalid component code', { WSComponentCode: ComponentCode }, { tblPMWorkSheet: PMWorksheetID })
                continue;
            }
            if (swCompEqptWPStored.indexOf(comp.swCompanyEqptKey) !== -1) {
                logger.error('Repeating job for '+ComponentCode+' '+PMWorksheetID)
                continue;
            }

            const wpfId = pmMap[PMWorksheetID]
            const {
                ID: swWorkProcFleetKey,
                stdTasks,
                RescheduleType,
                IntervalCalUnit,
                IntervalCal,
                WpID,
                mwcRoleTypeKey,
                IntervalRH,
                IsEqptOverhaul,
            } = swWorkProcFleet.by('ID', wpfId)

            let uq = [comp.ID+'-'+swWorkProcFleetKey]
            if (typeof swWorkProcIDinserted[uq] !== 'undefined') {
                swWorkProc.insert({
                    PMWorksheetID,
                    ID: swWorkProcIDinserted[uq],
                    swWorkProcFleetKey,
                })
                continue;
            }


            swCoEqptWPInserts.push({
                ID: ++swCoEqptWPID,
                swWorkProcFleetKey,
                swCompanyEqptKey: comp.swCompanyEqptKey,
            })
            const ID = ++swWorkProcID
            swWorkProcIDinserted[uq] = ID
            swWorkProcInserts.push({
                ID,
                mwcSitesKey,
                Title: Title.substring(0, 50),
                Description,
                RescheduleType,
                IntervalRH,
                IntervalCalUnit,
                IntervalCal,
                IsEqptOverhaul,
                ScheduleBasis: 'C',
                mwcRoleTypeKey,
                WpID,
            })

            swWpStdTaskInserts.push(...stdTasks.map((smStdTaskKey, i) => ({
                ID: ++swWpStdTaskID,
                smStdTaskKey,
                swWorkProcKey: ID,
                DisplaySequence: i+1,
                mwcSitesKey,
            })))

            swEqptWorkProcInserts.push({
                // ID: ++swEqptWorkProcID,
                ID,
                mwcSitesKey,
                swEquipmentKey: comp.ID,
                ProcedureID,
                // swWorkProcKey: ID,
                // NextScheduledDate,
                // NextScheduledRH,
                // swRegAgencyKey,
                swRHMeterKey: IntervalRH ? comp.swRhMeterKey : null,
                // OpenSwEqptHistoryKey,
                // swEqptWorProcSpecialType,
                RegulationID,
                LastCompleted,
                LastCompletedRH,
                swWorkProcFleetKey,
                Deactivate: !Active,
            })

            await swWorkProc.insert({
                PMWorksheetID,
                ID,
                swWorkProcFleetKey,
            })
        }
        await batchStore(swWorkProcInserts, tt.swWorkProc)
        await batchStore(swWpStdTaskInserts, tt.swWpStdTask)
        await batchStore(swEqptWorkProcInserts, tt.swEqptWorkProc)

        // const swEqptHistory = new Datastore({ inMemoryOnly: true });
        const swEqptHistoryInserts = []
        let swEqptSparesHistoryInserts = []
        let swSparesAdjustInserts = []

        let qmc = source.from({ mc: 'tblMaintenanceCompleted' })
        // .leftJoin({ mcs: 'tblMaintenanceCompletedClassSurvey' }, 'mcs.MaintenanceCompletedID','mc.MaintenanceCompletedID', )
        .innerJoin({ c: 'tblComponent' }, 'mc.ComponentCode', 'c.ComponentCode')
        .leftJoin({ p: 'tblPMWorksheet' }, 'mc.PMWorksheetID', 'p.PMWorksheetID')
        .select([
            'mc.ComponentCode',
            'mc.PMWorksheetID',
            'mc.MaintenanceCompletedID',
            'mc.BriefJobDescription',
            'mc.DateCompleted',
            'mc.HoursCompleted',
            'mc.Status',
            'mc.IssuedTo',
            'mc.Unscheduled',
            'mc.Created',
            'mc.DateDue',
            'mc.EngineerReport',
            'mc.ManHours',
            'mc.DeferDate',
            'mc.DeferReason',
            'mc.ReturnedDate',
            'mc.SafetyNotes',
            'mc.SpecialTools',
            'mc.FullJobText',
            // 'mcs.ClassSocietySurveyCode as cssc',
            'p.PreviousEngineerReport as ShipEqptWPNotes' 
        ])

        const classSociety = _.keyBy(await source.from('tblMaintenanceCompletedClassSurvey').select(), 'MaintenanceCompletedID');


        const puAll = _.groupBy(await source.from('tblPartUsed').select(), 'MaintenanceCompletedID')

        const cleanMC = async () => {
            await batchStore(swEqptHistoryInserts, tt.swEqptHistory)
            await batchStore(swEqptSparesHistoryInserts, tt.swEqptSparesHistory)
            await batchStore(swSparesAdjustInserts, tt.swSparesAdjust)
        }

        restartQueryLog("7.updateShipEqptWPNotes");

        for await (let mc of iterate(source, qmc, 'mc.MaintenanceCompletedID', null, 500)) {
            let {
                ComponentCode,
                PMWorksheetID,
                MaintenanceCompletedID,
                BriefJobDescription: Title,
                DateCompleted,
                HoursCompleted: RhReading,
                Status,
                IssuedTo: IssueTo,
                Unscheduled: IsUnscheduledMaintenance,
                Created: DateAssigned,
                DateDue,
                EngineerReport: CompletedDetails,
                ManHours,
                // cssc: RegulationID,
                DeferDate: DateDeferred,
                DeferReason: DeferredReason,
                ReturnedDate: DateRejected,
                SafetyNotes,
                SpecialTools,
                FullJobText,
                ShipEqptWPNotes
            } = mc
            let RegulationID = classSociety[MaintenanceCompletedID] || ''
            
            if (!ComponentCode) continue;

            let swWorkProcKey = null
            let swWorkProcFleetKey = null
            if (PMWorksheetID) {
                const wp = swWorkProc.by('PMWorksheetID', PMWorksheetID)
                if (!wp) {
                    InvalidReferenceError.log('MC: Invalid WS ID', { PMWorksheetID }, { tblMaintenanceCompleted: MaintenanceCompletedID })
                    continue
                }
                swWorkProcKey = wp.ID
                swWorkProcFleetKey = wp.swWorkProcFleetKey
            }

            const comp = site.swEquipment.by('ComponentCode', ComponentCode)
            if (!comp) {
                InvalidReferenceError.log('MC: Invalid ComponentCode', { MCComponentCode: ComponentCode }, { tblMaintenanceCompleted: MaintenanceCompletedID })
                continue
            }

            const WorkDetails = [
                SafetyNotes,
                SpecialTools,
                FullJobText,
            ].join(' ').trim()

            const ID = ++swEqptHistoryID

            let mwcRoleTypeKey = null
            if (roleTypes[IssueTo]) mwcRoleTypeKey = roleTypes[IssueTo].ID
            else if (roleConvert[IssueTo]) mwcRoleTypeKey = roleConvert[IssueTo]
            else mwcRoleTypeKey = 90000008

            swEqptHistoryInserts.push({
                ID,
                swWorkProcKey,
                swWorkProcFleetKey,
                Title: Title.substring(0, 75),
                swEquipmentKey: comp.ID,
                swEqptWorkProcKey: swWorkProcKey,
                DateCompleted,
                RhReading,
                EqptName: comp.EqptName.substring(0, 75),
                EqptSerialNo: comp.SerialNo,
                WorkDetails,
                mwcRoleTypeKey,
                IsCancelled: Status === 'I',
                IsJobComplete: (Status === 'C') || (Status === 'I'),
                IsUnscheduledMaintenance,
                // swCapitalProjectKey,
                IsEqptOverhaul: Title.toLowerCase().indexOf('overhaul') !== -1,
                DateAssigned,
                DateDue,
                mwcSitesKey,
                CompletedDetails,
                ManHours,
                DateIssued: DateAssigned,
                RegulationID,
                DateDeferred,
                DeferredReason,
                DateRejected,
                ShipEqptWPNotes
            })

            // 7.updateShipEqptWPNotes
            // DATE: March 17 2023 - Donald - Datafac
            // ISSUE: swEqptHistory.ShipEqptWPNotes is missing
            // SOLUTION: Create SQL query then run SQL query in database
            const buildShipEqptWPNotesSQL = (ID, ShipEqptWPNotes) => {
                const q = target(tt.swEqptHistory).where('ID', '=', ID).update({ ShipEqptWPNotes })
                logQuery(q.toString().replace(";select @@rowcount", ";"));
            }
            
            buildShipEqptWPNotesSQL(ID, ShipEqptWPNotes);
            // END 7.updateShipEqptWPNotes

            const pu = _.get(puAll, MaintenanceCompletedID, [])
            for (let {
                PartNumber,
                ComponentCode,
                DateUsed,
                QtyUsed,
                Reason: comments,
            } of pu) {
                const sp = _.head(site.swSpares.find({ PartNumber, ComponentCode }))
                if (!sp) {
                    InvalidReferenceError.log('PU: Invalid Part Number', {
                        MaintenanceCompletedID,
                        PartNumber,
                        ComponentCode,
                    })
                    continue
                }

                const spID = ++swSparesAdjustID

                swEqptSparesHistoryInserts.push({
                    ID: spID,
                    swEquipmentKey: comp.ID,
                    swEqptHistoryKey: ID,
                    swSparesKey: sp.ID,
                    DateUsed,
                    QtyUsed,
                    // CurrentUnitPrice,
                    // IsAdjustInventory,
                    mwcSitesKey,
                })

                swSparesAdjustInserts.push({
                    ID: spID,
                    swSparesKey: sp.ID,
                    swEquipmentKey: comp.ID,
                    swSparesAdjustTypeKey: 2,
                    DateTimeAdjusted: DateUsed,
                    QtyAdjust: QtyUsed,
                    // OldQtyOnHand,
                    // OldUnitPrice,
                    // NewUnitPrice,
                    // OldAvgPrice,
                    // NewAvgPrice,
                    // swEmployeeKey,
                    comments,
                    swEqptSparesHistoryKey: spID,
                    mwcSitesKey,
                })
            }

            if (swEqptHistoryInserts.length > 5000) {
                console.log(`cleanMC`)
                await cleanMC()
            }
        }

        await cleanMC()
    }
    await batchStore(_.uniqBy(swCoEqptWPInserts, ({swWorkProcFleetKey, swCompanyEqptKey}) => {
        return `${swWorkProcFleetKey}-${swCompanyEqptKey}`
    }), tt.swCoEqptWP)

    console.log('ALL DONE')
}

main().then(() => process.exit())


// update swWorkProcFleet
// set swWorkProcFleet.WpID = (
//
//     select top(1) CONCAT(sub.JobCode, '-' , RIGHT(CONCAT('000',CAST(sub.rn AS VARCHAR(3))),4)) from (
//
//     select wp.ID, wp.WpID,
//     cw.swCompanyEqptKey, SUBSTRING(wp.WpID, 0,5) as JobCode,
//     row_number () over (
//     partition by cw.swCompanyEqptKey, SUBSTRING(wp.WpID, 0,5) order by wp.ID
// ) rn
//
// from swWorkProcFleet wp
// inner join swCoEqptWP cw
// on cw.swWorkProcFleetKey = wp.ID
// ) sub where sub.ID = swWorkProcFleet.ID
// )
Editor is loading...