'use strict' ; import { QuerySyntaxError } from './Errors' ; import { X12Interchange } from './X12Interchange' ; import { X12QueryEngine } from './X12QueryEngine' ; import { X12Transaction } from './X12Transaction' ; import { TxEngine } from './X12SerializationOptions' ; import * as nodeRequire from '../nodeRequire.js' ; import * as crypto from 'crypto' ; private description param { any } val returns { boolean } function isEmpty ( val ) { if ( val === undefined || val === null ) return true ; if ( typeof val === 'string' && val . trim ( ) === '' ) return true ; return false ; } export class X12TransactionMap { description param { object } map param { X12Transaction } transaction param { Function | 'liquidjs' | 'internal' } helper param { 'liquidjs' | 'internal' } txEngine constructor ( map , transaction ? , helper ? , txEngine ? ) { this . _map = map ; this . _transaction = transaction ; this . helper = typeof helper === 'function' ? helper : this . _helper ; if ( typeof helper === 'string' && typeof txEngine === 'undefined' ) { txEngine = helper ; } this . txEngine = txEngine === undefined ? 'internal' : txEngine ; } protected _map : any ; protected _transaction : X12Transaction ; protected _object : any ; helper : Function ; txEngine : TxEngine ; description param { X12Transaction } transaction param { Function } helper setTransaction ( transaction , helper ? ) { this . _transaction = transaction ; this . helper = helper === undefined ? this . _helper : helper ; } description returns { X12Transaction } getTransaction ( ) { return this . _transaction ; } description param { object } map param { Function } callback returns { object | object [ ] } toObject ( map ? , callback ? ) { map = map === undefined ? this . _map : map ; const clone = JSON . parse ( JSON . stringify ( map ) ) ; let clones = null ; const engine = new X12QueryEngine ( false ) ; const interchange = new X12Interchange ( ) ; interchange . setHeader ( [ '00' , '' , '00' , '' , 'ZZ' , '00000000' , '01' , '00000000' , '000000' , '0000' , '|' , '00000' , '00000000' , '0' , 'P' , '>' ] ) ; interchange . addFunctionalGroup ( ) . transactions = [ this . _transaction ] ; Object . keys ( map ) . forEach ( key => { if ( Object . prototype . hasOwnProperty . call ( map , key ) as boolean ) { if ( Array . isArray ( map [ key ] ) && typeof map [ key ] [ 0 ] === 'string' ) { const typedArray = map [ key ] as string [ ] ; const newArray = new Array < string | string [ ] > ( ) ; typedArray . forEach ( query => { try { const result = engine . querySingle ( interchange , query , '' ) ; if ( result === null ) { newArray . push ( null ) ; } else if ( result . value === null || Array . isArray ( clone [ key ] [ 0 ] ) ) { if ( result . value !== null ) { clone [ key ] . forEach ( ( array ) => { array . push ( this . helper ( key , result . value , query , callback ) ) ; } ) ; } else { let superArray = new Array < string [ ] > ( ) ; if ( Array . isArray ( clone [ key ] [ 0 ] ) ) { superArray = clone [ key ] ; } result . values . forEach ( ( value , index ) => { if ( ! Array . isArray ( superArray [ index ] ) ) { superArray [ index ] = new Array < string > ( ) ; } superArray [ index ] . push ( this . helper ( key , value , query , callback ) ) ; } ) ; newArray . push ( ... superArray ) ; } } else { newArray . push ( this . helper ( key , result . value , query , callback ) ) ; } } catch ( err ) { throw new QuerySyntaxError ( `${ err . message }; bad query in ${ map [ key ] }` ) ; } } ) ; clone [ key ] = newArray ; } else if ( typeof map [ key ] === 'string' ) { try { const result = engine . querySingle ( interchange , map [ key ] , '' ) ; if ( result === null ) { clone [ key ] = null ; } else if ( result . value === null || Array . isArray ( clones ) ) { if ( result . value !== null ) { clones . forEach ( ( cloned ) => { cloned [ key ] = this . helper ( key , result . value , map [ key ] , callback ) ; } ) ; } else { if ( ! Array . isArray ( clones ) ) { clones = new Array < object > ( ) ; } result . values . forEach ( ( value , index ) => { if ( clones [ index ] === undefined ) { clones [ index ] = JSON . parse ( JSON . stringify ( clone ) ) ; } clones [ index ] [ key ] = this . helper ( key , value , map [ key ] , callback ) ; } ) ; } } else { clone [ key ] = this . helper ( key , result . value , map [ key ] , callback ) ; } } catch ( err ) { throw new QuerySyntaxError ( `${ err . message }; bad query in ${ map [ key ] }` ) ; } } else { clone [ key ] = this . toObject ( map [ key ] ) ; } } } ) ; return Array . isArray ( clones ) ? clones : clone ; } description param { object } input param { object } map param { object } macroObj returns { X12Transaction } fromObject ( input , map ? , macroObj = { } ) { const counter = { } ; const levels = { } ; let liquidjs ; const macro = { counter : { } , currentDate : `${ new Date ( ) . getFullYear ( ) }${ ( new Date ( ) . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' ) }${ new Date ( ) . getDate ( ) . toString ( ) . padStart ( 2 , '0' ) }` , sequence : function sequence ( value ) { if ( macro . counter [ value ] === undefined ) { macro . counter [ value ] = 1 ; } else { macro . counter [ value ] += 1 ; } return { val : macro . counter [ value ] } ; } , json : function json ( value ) { return { val : JSON . parse ( value ) } ; } , length : function length ( value ) { return { val : value . length } ; } , map : function ( value , property ) { return { val : value . map ( item => item [ property ] ) } ; } , sum : function ( value , property , dec ) { let sum = 0 ; value . forEach ( ( item ) => { sum += item [ property ] ; } ) ; return { val : sum . toFixed ( dec === undefined ? 0 : dec ) } ; } , random : function random ( ) { return { val : Math . floor ( 1000 + Math . random ( ) * 10000 ) } ; } , truncate : function truncate ( value , maxChars ) { if ( Array . isArray ( value ) ) { value = value . map ( str => str . substring ( 0 , maxChars ) ) ; } else { value = `${ value }` . substring ( 0 , maxChars ) ; } return { val : value } ; } } ; const LIQUID_FILTERS = { hl_root : ( value , depth = 0 ) => { if ( counter [ value ] === undefined ) { counter [ value ] = 0 ; } counter [ value ] += 1 ; levels [ value ] [ depth ] = counter [ value ] ; return counter [ value ] ; } , hl_parent : ( value , depth ) => { if ( levels [ value ] [ depth ] === undefined ) { return 1 ; } return levels [ value ] [ depth ] ; } , sequence : ( value ) => { if ( counter [ value ] === undefined ) { counter [ value ] = 1 ; } else { counter [ value ] += 1 ; } return counter [ value ] ; } , sum_array : ( value ) => { if ( typeof value === 'undefined' ) return 0 ; let sum = 0 ; value . forEach ( ( item ) => { sum += item ; } ) ; return sum ; } , in_loop : ( value ) => { return LIQUID_FILTERS . json_stringify ( value ) ; } , json_stringify : ( value ) => { return JSON . stringify ( value ) ; } , json_parse : ( value ) => { if ( typeof value === 'undefined' ) return '' ; return JSON . parse ( value ) ; } , truncate : ( value , maxChars ) => { if ( typeof value === 'undefined' ) return '' ; if ( Array . isArray ( value ) ) { return value . map ( str => { if ( typeof str === 'undefined' ) return '' ; return str . substring ( 0 , maxChars ) ; } ) ; } return `${ value }` . substring ( 0 , maxChars ) ; } , random : ( val , maxLength = 4 ) => { const bytes = Math . ceil ( maxLength / 2 ) ; const buffer = crypto . randomBytes ( bytes ) ; const hex = buffer . toString ( 'hex' ) ; return parseInt ( hex , 16 ) . toString ( ) . substring ( 0 , maxLength ) ; } , edi_date : ( val , length = 'long' ) => { const date = ! isEmpty ( val ) ? new Date ( val ) : new Date ( ) ; const ediDate = `${ date . getUTCFullYear ( ) }${ ( date . getUTCMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' ) }${ date . getUTCDate ( ) . toString ( ) . padStart ( 2 , '0' ) }` ; if ( length !== 'long' ) { return ediDate . substring ( 2 , ediDate . length ) ; } return ediDate ; } , edi_time : ( val ) => { const date = ! isEmpty ( val ) ? new Date ( val ) : new Date ( ) ; return `${ date . getUTCHours ( ) . toString ( ) . padStart ( 2 , '0' ) }${ date . getUTCMinutes ( ) . toString ( ) . padStart ( 2 , '0' ) }` ; } } ; const resolveKey = function resolveKey ( key ) : any { if ( typeof liquidjs !== 'undefined' ) { const result = liquidjs . parseAndRenderSync ( key , { input } ) ; return key . indexOf ( 'in_loop }}' ) > - 1 ? JSON . parse ( result ) : result ; } else { const clean = /(^(`\${)*(input|macro)\[.*(}`)*$)/g ; if ( clean . test ( key ) ) { const result = eval ( key ) ; return result === undefined ? '' : result ; } else { return key ; } } } ; const resolveLoop = function resolveLoop ( lp , tx ) : void { const start = lp [ 0 ] ; const length = parseFloat ( resolveKey ( start . loopLength ) ) ; for ( let i = 0 ; i < length ; i += 1 ) { lp . forEach ( segment => { const elements = [ ] ; for ( let j = 0 ; j < segment . elements . length ; j += 1 ) { const resolved = resolveKey ( segment . elements [ j ] ) ; if ( Array . isArray ( resolved ) ) { elements . push ( resolved [ i ] ) ; } else { elements . push ( resolved ) ; } } tx . addSegment ( segment . tag , elements ) ; } ) ; } } ; if ( map !== undefined ) { if ( map . header === undefined || map . segments === undefined ) { macroObj = map ; map = undefined ; } } map = map === undefined ? this . _map : map ; if ( this . txEngine === 'liquidjs' ) { const { Liquid } = nodeRequire ( 'liquidjs' ) ; const engine = new Liquid ( { strictFilters : true } ) ; for ( const [ name , func ] of Object . entries ( LIQUID_FILTERS ) ) { engine . registerFilter ( name , func ) ; } if ( typeof macroObj === 'object' ) { for ( const [ name , func ] of Object . entries ( macroObj ) ) { if ( typeof name === 'string' && typeof func === 'function' ) { engine . registerFilter ( name , func ) ; } } } liquidjs = engine ; } else { Object . assign ( macro , macroObj ) ; } const transaction = this . _transaction === undefined ? new X12Transaction ( ) : this . _transaction ; const header = [ ] ; let looper = [ ] ; let loop = false ; for ( let i = 0 ; i < map . header . length ; i += 1 ) { header . push ( resolveKey ( map . header [ i ] ) ) ; } transaction . setHeader ( header ) ; for ( let i = 0 ; i < map . segments . length ; i += 1 ) { const segment = map . segments [ i ] ; const elements = [ ] ; if ( segment . loopStart as boolean ) { looper . push ( segment ) ; loop = true ; } else if ( loop ) { looper . push ( segment ) ; } if ( ! loop ) { for ( let j = 0 ; j < segment . elements . length ; j += 1 ) { elements . push ( resolveKey ( segment . elements [ j ] ) ) ; } transaction . addSegment ( segment . tag , elements ) ; } if ( segment . loopEnd as boolean ) { resolveLoop ( looper , transaction ) ; looper = [ ] ; loop = false ; } } this . _transaction = transaction ; return transaction ; } private description param { string } key param { string } value param { string } query param { Function } callback returns { string } private _helper ( key , value , query ? , callback ? ) { if ( callback !== undefined ) { callback ( key , value , query ) ; } return value ; } }