import { DelhiveryAuthentication } from "./authentication"; import { MyGlobal as app } from "../globals"; import { Pieces, ReqAddress, DelhiveryRequest, WeightUnit, ReasonForExport, DimensionUnit, FLCLPackage, FlavorcloudShipmentResponse, DelhiveryLabelResponse } from "./shipments.types"; import { FlclError } from "fc-logger"; import { currentDate, formatTimestampToDate, roundOffDecimals, sleep ,extractErrMessage } from "./utility"; import { CHECK_PACKAGE_STATUS_SLEEP_TIME, clientName, FOB_Countries, gstin, MAX_RETRY_TIME, sellerId, wareHouseId } from "../constants"; import convert from 'convert-units'; import { uploadBufferToAWSCdn } from "../helper/cdnUpload"; const { merge } = require('merge-pdf-buffers'); export class Shipments { constructor() { } async createShipments(RequestData: any) { try { const data: DelhiveryRequest = this.createBuildRequest(RequestData) const Authenticator = new DelhiveryAuthentication(); await Authenticator.authenticate(); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'data', propertyValue: data }); const createResponse = await app.axios?.post(`${app.create_package_url}`, data); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'createResponse', propertyValue: { createResponse: createResponse?.data } }); if (!createResponse?.data?.payload?.id || createResponse?.data?.message !== "ok") throw FlclError.create({ className: 'Delhivery', methodName: 'createShipments', message: 'Something went wrong while creating the package in Delhivery', propertyName: 'createResponse', propertyValue: { createResponse: createResponse?.data } }); const delhiveryPackageID = createResponse.data.payload.id; const packageStatusURL = app.package_status_url?.replace('#PCK_ID#', delhiveryPackageID); let packageStatusResponse; let retryTimeInMS = 0; while (packageStatusResponse?.data.payload.status !== "COMPLETED" && retryTimeInMS < MAX_RETRY_TIME) { packageStatusResponse = await app.axios?.get(`${packageStatusURL}`); app.logger?.debug({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'packageStatusResponse', propertyValue: { packageStatusResponse: packageStatusResponse?.data } }); if (packageStatusResponse?.data.payload.status !== "COMPLETED") { await sleep(CHECK_PACKAGE_STATUS_SLEEP_TIME); retryTimeInMS += CHECK_PACKAGE_STATUS_SLEEP_TIME; } } if ( !packageStatusResponse?.data?.payload || packageStatusResponse?.data?.payload?.data.error_count > 0 || packageStatusResponse?.data.payload.status !== "COMPLETED" ){ var errorMessage = packageStatusResponse?.data?.payload?.data?.error_waybills[0]?.reason || `Something went wrong while fetching the package status from Delhivery` ; throw FlclError.create({ className: 'Delhivery', methodName: 'createShipments', message: extractErrMessage(errorMessage) , propertyName: 'packageStatusResponse', propertyValue: { packageStatusResponse: packageStatusResponse?.data, didTimeout: retryTimeInMS < MAX_RETRY_TIME, retryTimeInMS } }); } const waybillNumber = packageStatusResponse?.data?.payload?.data.success_waybills[0].waybill; const labelURL = app.label_url?.replace("#WAYBILL_NO#", waybillNumber); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'packageStatusResponse', propertyValue: { packageStatusResponse: packageStatusResponse.data, labelURL } }); //await sleep(2000); const waybillResponse = await app.axios?.get(`${labelURL}`, { headers: { 'accept': 'application/pdf' }, responseType: "arraybuffer" }); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'waybillResponse', propertyValue: { waybillResponse: waybillResponse?.data } }); if (!waybillResponse?.data) throw FlclError.create({ className: 'Delhivery', methodName: 'createShipments', message: `No data found in waybillResponse`, propertyName: 'waybillResponse', propertyValue: { waybillResponse: waybillResponse?.data } }); const labelUrl = await uploadBufferToAWSCdn(waybillResponse.data, waybillNumber); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'labelUrl', propertyValue: { labelUrl: labelUrl } }); const invoiceURL = app.invoice_url?.replace("#WAYBILL_NO#", waybillNumber); //await sleep(2000); const invoiceResponse = await app.axios?.get(`${invoiceURL}`, { headers: { 'accept': 'application/pdf' }, responseType: "arraybuffer" }); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'invoiceResponse', propertyValue: { invoiceResponse: invoiceResponse?.data } }); if (!invoiceResponse?.data) throw FlclError.create({ className: 'Delhivery', methodName: 'createShipments', message: `No data found in invoiceResponse`, propertyName: 'invoiceResponse', propertyValue: { invoiceResponse: invoiceResponse?.data } }); const auth_letter_url = app.auth_letter_url?.replace("#WAYBILL_NO#", waybillNumber); //await sleep(2000); const authLetterResponse = await app.axios?.get(`${auth_letter_url}`, { headers: { 'accept': 'application/pdf' }}); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'authLetterResponse', propertyValue: { authLetterResponse: authLetterResponse?.data } }); if (!authLetterResponse?.data) throw FlclError.create({ className: 'Delhivery', methodName: 'createShipments', message: `No data found in authLetterResponse`, propertyName: 'authLetterResponse', propertyValue: { authLetterResponse: authLetterResponse?.data } }); //var aut_arr= Uint8Array.from(atob(authLetterResponse.data.payload.base64_pdf), c => c.charCodeAt(0)) const myBuffer = Buffer.from(authLetterResponse.data.payload.base64_pdf, 'base64'); var aut_arr = Uint8Array.from(myBuffer) var inv_arr = Uint8Array.from(invoiceResponse.data) var final_arr = await merge([ inv_arr , aut_arr]) let isInvoice = true const invoiceUrl = await uploadBufferToAWSCdn(final_arr, waybillNumber,isInvoice); app.logger?.info({ className: 'Delhivery', methodName: 'createShipments', propertyName: 'invoiceUrl', propertyValue: { invoiceUrl: invoiceUrl } }); return this.buildResponse({ waybillNumber, label: labelUrl,invoice : invoiceUrl}, RequestData); } catch (e) { app.logger?.error({ className: 'Delhivery', methodName: 'createShipments', message: 'Error while getting labels from Delhivery', cause: e }) throw e; } } private createBuildRequest(data: any): DelhiveryRequest { return { packages: [ { weight: this.getWeight(data.total_weight, data.weight_unit.toLowerCase()), dims: this.getDimensions(data.package, data.dimension_unit.toLowerCase()), package_amount: data.shipment_total, total_commodity_value: data.shipment_total, battery: false, shipment_type: this.getShipmentType(data.reason_for_export), currency: data.currency, order_no: (Date.now() + Math.random()).toString(36).replace('.', ''), //FIXME: for testing //`${data.flcl_order_id}`, client_name: app.clientName || clientName, transaction_type: "B2C", payment_mode: "Prepaid", service_type: "EXPORTS_EXPRESS", invoice: { number: data.reference.toString(), terms: this.getInvoiceTerms(data.shipto_address, this.getShipmentType(data.reason_for_export)), date: currentDate(), //TODO: Check formatTimestampToDate(data.invoice_date), igst_payment_status: "Paid", shipping_charge: data.rate_api_shiping, insurance_charge: 0 }, pickup_location: { pickup_warehouse_id: app.wareHouseId || wareHouseId, country: "IN" }, delivery_location: this.getDeliveryLocation(data.shipto_address), clearance_mode: "courier", products: this.getProductDetails(data.pieces), consignor: this.getConsignor(data.shipfrom_address,data?.metadata?.india_export_info), //app.isProd ? this.getConsignorSellerID() : this.getConsignor(data.shipfrom_address), consignee: this.getConsignee(data.shipto_address) } ] }; } private getAddress(address: ReqAddress) { return `${address.address_line_1}${address.address_line_2 && `,${address.address_line_2}`}${address.address_line_3 && `,${address.address_line_3}`}` } private getDeliveryLocation(address: ReqAddress) { return { address: this.getAddress(address), city: address.city, state: address.state, country: address.country, zip: `${address.zip}` } } private getProductDetails(pieces: Array) { return pieces.map((piece) => { return { desc: piece.description, quantity: piece.quantity, item_commodity_value: piece.sale_price, unit_price: piece.sale_price, total_amount: piece.sale_price, hsn_code: `${piece.hs_code}`, product_amount: piece.sale_price } }) } private getInvoiceTerms(address: ReqAddress, shipmentType: string) { if (shipmentType == 'gift' || shipmentType == 'sample') return "FOB"; if (FOB_Countries.includes(address.country)) return "FOB"; return "CIF"; } private getConsignee(address: ReqAddress) { return { name: this.getName(address), email: address.email, phone: `${address.phone}` } } private getConsignorSellerID() { return { seller_id: sellerId } } private getConsignor(address: ReqAddress , indiaExportInfo:any) { return { name: this.getName(address), phone: `${address.phone}`, email: address.email, type: "Office", address: this.getAddress(address), city: address.city, state: address.state, country: address.country, zip: `${address.zip}`, document_id: indiaExportInfo?.document_id, document_type: indiaExportInfo?.document_type, gstin: indiaExportInfo?.gst, iec:indiaExportInfo?.iec, pan:indiaExportInfo?.pan, bank_ad_code:indiaExportInfo?.bank_ad_code, bank_ifsc:indiaExportInfo?.bank_ifsc_code, bank_ac:indiaExportInfo?.bank_account_number } } private getShipmentType(shipmentType: ReasonForExport) { enum ShipmentType { 'documents' = 'document', 'gift' = 'gift', 'merchandise' = 'commercial', 'sample' = 'sample', 'other' = 'commercial' } return ShipmentType[shipmentType] || 'sample'; } private getDimensions(packageDim: FLCLPackage, dimensionUnit: DimensionUnit) { return { length: this.convertDim(packageDim.length, dimensionUnit), width: this.convertDim(packageDim.width), height: this.convertDim(packageDim.height) } } private convertDim(value: number, unit: DimensionUnit = 'cm') { return roundOffDecimals(convert(value).from(unit).to('cm')); } getWeight(weight: number, weightUnit: WeightUnit = 'kg') { if (weightUnit !== 'kg') weight = convert(weight).from(weightUnit).to('kg'); if (weight < 1) return 1 return roundOffDecimals(weight); } private getName(address: ReqAddress) { return address.name || address.address_line_3; } private buildResponse(data: DelhiveryLabelResponse, args: any): FlavorcloudShipmentResponse { try { let response: FlavorcloudShipmentResponse = { provider_name: 'Delhivery', shipping_provider: 'Delhivery', carrier: 'Delhivery', tracking_number: data.waybillNumber, provider_tracking_number: data.waybillNumber, shipping_label: data.label, formUrl: data.invoice, label_type: "CDN", gross_weight: args.total_weight, gross_weight_unit: args.weight_unit, submitted_electronically: true }; app.logger?.info({ className: 'Delhivery', methodName: 'buildResponse', propertyName: 'FlavorcloudShipmentResponse', propertyValue: response }); return response; } catch (e) { throw e; } } }