Convert all Sales Order to Invoice against Cash Sale Generation

/**
 *@NApiVersion 2.1
 *@NScriptType MapReduceScript
 */

define(['N/search', 'N/record', 'N/runtime', 'N/file', 'N/email'], function (search, record, runtime, file, email) {

    const SCRIPT_PARAM = "custscript_grw_005_search_id";
    const SCRIPT_PARAM_FOLDER_ID = "custscript_grw_005_csv_folder_id"
    const CUST_TO_BE_AUTOINV = "custrecord_grw_005_cust_auto_inv";
    const CUST_ORDER_SEARCH = "custrecord_grw_005_order_queue";
    const CUST_INV_BATCH_MAP_REC = "customrecord_grw_005_inv_batch_mapping"
    const CUST_ADYEN_AMT_OVERRIDE = "custrecord_grw_005_3_amt_override"
    const CUST_ADYEN_SUPPRESS_BILL_ADDRESS = "custrecord_grw_005_3_suppres_billaddress"
    const CUST_CHILD_REC_CURRENCY = "custrecord_grw_005_4_parent.custrecord_grw_005_4_tran_currency.id"
    const CUST_CHILD_REC_ADYEN_PBL_CONFIG = "custrecord_grw_005_4_parent.custrecord_grw_005_4_adn_pbl_config.id"

    const ADYEN_AMT_OVERRIDE = "custbody_es_adyen_auth_amount_override"
    const ADYEN_SUPPRESS_BILL_ADDRESS = "custbody_es_adyen_suppress_billaddress"
    const ADYEN_PAY_BY_LINK_CONFIG = "custbody_es_adyen_pbl_config"
    const ADYEN_PAY_BY_LINK_URL_COPY = "custbody_ota_paybylinkurlcopied"

    const ADYEN_PAY_BY_LINK_URL = "custbody_es_adyen_pbl_url"
    const ADYEN_PAY_BY_LINK_EXPIRES_ON = "custbody_es_adyen_pbl_expiry"
    const SO_EXTERNALID = "custbody_ota_salesforcerecordid"
    const INV_INVOICE_BATCH = "custbody_ota_invoicebatchcustomer"
    const OTA_SF_SYNC_STATUS = "custbody_ota_salesforcesynchstatus"
    const SF_SYNC_STATUS_VAL = "1"
    const SCRIPT_PARAM_SENDER_ID = "custscript_grw_005_sender_id"
    const SCRIPT_PARAM_RECIPIENT_ID = "custscript_grw_005_recipient_id"

    function getInputData() {

        try {
            let savedSeachId = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM });
            log.debug("savedSeachId", savedSeachId);

            let batchMapSeachObj = search.load({ id: savedSeachId });

            let batchMapSearchResults = [];

            batchMapSeachObj.run().each(function (result) {
                let otherField = result.getValue(CUST_TO_BE_AUTOINV);
                let orderSearchId;
                if (otherField) {
                    // Get the sales order search ID and any other data you need from the search result
                    orderSearchId = result.getValue(CUST_ORDER_SEARCH);
                }
                batchMapSearchResults.push({ orderSearchId });
                return true;
            });
            log.debug("batchMapSearchResults", batchMapSearchResults)

            let soDetails = []
            for (let index = 0; index < batchMapSearchResults.length; index++) {

                let soSearch = batchMapSearchResults[index].orderSearchId

                let orderList = getOrderList(soSearch);

                if (!isEmpty(orderList)) {
                    soDetails = soDetails.concat(orderList)
                }
            }
            log.debug("soDetails", soDetails)

            return soDetails;

        } catch (Err) {
            log.debug("Error @getInputData on record save", Err)
            log.error("Error @getInputData on record save", Err)
        }
    }

    function reduce(context) {
        let successCount = 0
        let errCount = 0
        let errorMessage = ""
        let soId
        try {
            log.debug("ENTERED REDUCE", "-----------ENTERED REDUCE---------------")

            let soKey = context.key;

            let orderList = JSON.parse(context.values);
            log.debug("orderList", orderList)

            soId = orderList.internalId
            log.debug("soId", soId)

            let soDocNum = orderList.tranId
            let soDocDate = orderList.docDate
            let extId = (orderList.externalId != "- None -") ? orderList.externalId : ""
            let sopblUrl = (orderList.pblUrl != "- None -") ? orderList.pblUrl : ""
            let termsId = (orderList.customerTerms == "- None -" || isEmpty(orderList.customerTerms)) ? "" : Number(orderList.customerTerms)
            let customerInvBatch = orderList.invBatch

            if (soId == "10499") {
                try {
                    let id = record.submitFields({
                        type: record.Type.SALES_ORDER,
                        id: soId,
                        values: {
                            terms: termsId,
                            paymentoption: ""
                        },
                        options: {
                            enableSourcing: false,
                            ignoreMandatoryFields: true
                        }
                    });
                    log.debug("Entered to Transformation of so:", id)

                    let soRecObj = record.load({
                        type: record.Type.SALES_ORDER,
                        id: soId,
                        isDynamic: true,
                    });
                    let soLineCount = soRecObj.getLineCount({
                        sublistId: 'item'
                    })

                    let soArray = []
                    for (let i = 0; i < soLineCount; i++) {

                        let solineIndex = i

                        let soOrderLine = soRecObj.getSublistValue({
                            sublistId: 'item',
                            fieldId: 'line',
                            line: i
                        });
                        let soTaxAmt = soRecObj.getSublistValue({
                            sublistId: 'item',
                            fieldId: 'tax1amt',
                            line: i
                        });

                        soArray.push({ soOrderLine, soTaxAmt, solineIndex })
                    }

                    let invRec = record.transform({
                        fromType: record.Type.SALES_ORDER,
                        fromId: id,
                        toType: record.Type.INVOICE
                    });

                    if (soDocNum) {
                        invRec.setValue({
                            fieldId: 'tranid',
                            value: soDocNum
                        })
                    }

                    if (extId) {
                        invRec.setValue({
                            fieldId: 'externalid',
                            value: extId
                        })
                    }

                    if (soDocDate) {
                        invRec.setText({
                            fieldId: 'trandate',
                            text: soDocDate
                        })
                    }

                    if (sopblUrl) {
                        invRec.setValue({
                            fieldId: ADYEN_PAY_BY_LINK_URL_COPY,
                            value: sopblUrl
                        })
                    }

                    if (customerInvBatch) {
                        invRec.setValue({
                            fieldId: INV_INVOICE_BATCH,
                            value: customerInvBatch
                        })

                        let tranCurrency = null
                        tranCurrency = invRec.getValue({ fieldId: 'currency' })
                        log.debug("transaction Currency", tranCurrency)

                        let InvMapfieldSearchdata = getMappingRecordData(customerInvBatch, tranCurrency);
                        log.debug("InvMapfieldSearchdata", InvMapfieldSearchdata)

                        var foundObj = {}
                        if (!isEmpty(InvMapfieldSearchdata)) {

                            foundObj = InvMapfieldSearchdata.find(e => e.pblConfig != null);

                            if (!isEmpty(foundObj)) {
                                //To autogenerate Adyen PBL in invoices except in Zero amount invoices.
                                if (!isEmpty(sopblUrl)) {
                                    let invPblConfig = foundObj.pblConfig
                                    log.debug("invPblConfig", invPblConfig)
                                    if (!isEmpty(invPblConfig)) {
                                        invRec.setValue({
                                            fieldId: ADYEN_PAY_BY_LINK_CONFIG,
                                            value: invPblConfig
                                        })
                                    }
                                }
                            }
                            else {
                                foundObj = InvMapfieldSearchdata[0]
                                log.debug("foundObj", foundObj)
                            }

                            let adyenAmtOverride = foundObj.amtOverride
                            if (!isEmpty(adyenAmtOverride)) {
                                invRec.setValue({
                                    fieldId: ADYEN_AMT_OVERRIDE,
                                    value: adyenAmtOverride
                                })
                            }

                            let invSupBillAddress = foundObj.supBillAddress
                            if (!isEmpty(invSupBillAddress)) {
                                invRec.setValue({
                                    fieldId: ADYEN_SUPPRESS_BILL_ADDRESS,
                                    value: invSupBillAddress
                                })
                            }
                        }
                    }

                    invRec.setValue({
                        fieldId: OTA_SF_SYNC_STATUS,
                        value: SF_SYNC_STATUS_VAL
                    })

                    let invLineCount = invRec.getLineCount({
                        sublistId: 'item'
                    })

                    if (!isEmpty(soArray)) {
                        for (let index = 0; index < invLineCount; index++) {

                            let invOrderLine = invRec.getSublistValue({
                                sublistId: 'item',
                                fieldId: 'orderline',
                                line: index
                            });

                            let obj = soArray.find(obj => obj.soOrderLine == invOrderLine);
                            log.debug("obj", obj)

                            //set taxamount if orderline matches or null
                            if (!isEmpty(obj)) {
                                invRec.setSublistValue({
                                    sublistId: 'item',
                                    fieldId: 'tax1amt',
                                    line: index,
                                    value: obj.soTaxAmt
                                });
                            }
                        }
                    }

                    let invoiceRecId = invRec.save({
                        enableSourcing: true,
                        ignoreMandatoryFields: true
                    });
                    log.debug("invoiceRecId", invoiceRecId)

                    if (invoiceRecId) {
                        successCount++;
                        errorMessage = true
                    }
                } catch (Errin) {
                    errCount++;
                    errorMessage = Errin.message
                    log.debug("errorCount in Errin", errCount)
                    log.debug("Errin", errorMessage)
                }
            }
            log.debug("successCount", successCount)
            context.write({
                key: soId,
                value: errorMessage
            });
        } catch (Err) {
            errCount++;
            log.error("Error @reduce on record save", Err)
        }

    }

    function summarize(summary) {
        log.debug("summary", summary)
        let errorDetails = []
        let flag = 0
        let errCount = 0;
        let successCount = 0;
        let errorMsg1 = "Failed Transformation for these Orders,Reason\n";
        summary.output.iterator().each(function (key, value) {
            if (value != true && value != 'true') {
                flag = 1
                errCount++;
                errorDetails.push({ key: key, value: value });
                errorMsg1 += (key + ' , ' + value + '\n');
            }
            else {
                successCount++
            }
            return true;
        });
        log.debug("errorMsg1", errorMsg1)
        log.debug("successCount", successCount)
        log.debug("errCount", errCount)

        let csvContent = `Success Count,Error Count\n${successCount},${errCount}\n`;

        if (flag == 1 || errCount > 0) {
            csvContent += errorMsg1;
        }
        log.debug("csvContent", csvContent)

        let fileName = "bill_run_summary" + "_" + Math.floor(Date.now() / 1000) + ".csv";

        // Create a file object and save the CSV content
        let csvResponseFolder = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM_FOLDER_ID });
        log.debug("csvResponseFolder", csvResponseFolder)
        let fileObj = file.create({
            name: fileName,
            fileType: file.Type.CSV,
            contents: csvContent,
            folder: csvResponseFolder,
        });
        let fileId = fileObj.save();
        log.debug('CSV File ID', fileId);

        let senderId = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM_SENDER_ID });
        let recipientId = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM_RECIPIENT_ID });
        email.send({
            author: senderId,
            recipients: recipientId,
            subject: 'Auto-Invoicing Status Update',
            body: "<BR />Hi, <BR/> <BR/>This email is to notify you that Invoice creation has completed. <BR/> Please find the status in the attached document.",
            attachments: [fileObj],
            relatedRecords: {
                entityId: recipientId
            }
        });
    }

    function getOrderList(soSearch) {

        try {

            if (soSearch) {

                let orderSeachObj = search.load({ id: soSearch });

                let salesOrdersResults = [];

                orderSeachObj.run().each(function (result) {

                    let internalId = result.getValue({
                        name: "internalid",
                        summary: "GROUP"
                    });
                    let customerId = result.getValue({
                        name: "entity",
                        summary: "GROUP"
                    });
                    let tranId = result.getValue({
                        name: "tranid",
                        summary: "GROUP"
                    });
                    let externalId = result.getValue({
                        name: SO_EXTERNALID,
                        summary: "GROUP"
                    });
                    let pblUrl = result.getValue({
                        name: ADYEN_PAY_BY_LINK_URL,
                        summary: "GROUP"
                    });
                    let pblUrlExp = result.getValue({
                        name: ADYEN_PAY_BY_LINK_EXPIRES_ON,
                        summary: "GROUP"
                    });

                    let customerTerms = result.getValue({
                        name: "terms",
                        join: "customer",
                        summary: "GROUP"
                    });

                    let invBatch = result.getValue({
                        name: INV_INVOICE_BATCH,
                        summary: "GROUP"
                    });

                    let docDate = result.getValue({
                        name: "trandate",
                        summary: "GROUP"
                    });

                    salesOrdersResults.push({ internalId, customerId, tranId, externalId, pblUrl, pblUrlExp, customerTerms, invBatch, docDate })

                    return true;
                });

                return salesOrdersResults;
            }
            else {
                return
            }

        } catch (Err) {
            log.debug("Err@getOrderList", Err)
            log.error("Err@getOrderList", Err)
        }
    }

    /**
     * Find value of Adyen Amount override
     * @param {string} invBatchNumber 
     * @returns 
     */
    function getMappingRecordData(customerInvBatch, tranCurrency) {
        try {
            let InvMapfieldLookUp = [];

            let InvMapfieldSearchObj = search.create({
                type: CUST_INV_BATCH_MAP_REC,
                filters: ["name", "is", customerInvBatch],
                columns:
                    [
                        search.createColumn({ name: CUST_ADYEN_AMT_OVERRIDE }),
                        search.createColumn({ name: CUST_ADYEN_SUPPRESS_BILL_ADDRESS }),
                        search.createColumn({
                            name: "formulatext",
                            formula: `CASE WHEN {${CUST_CHILD_REC_CURRENCY}} = ${tranCurrency} THEN {${CUST_CHILD_REC_ADYEN_PBL_CONFIG}} ELSE NULL END`
                        })
                    ]
            });
            let searchResultCount = InvMapfieldSearchObj.runPaged().count;
            log.debug("InvMapfieldSearchObj result count", searchResultCount);
            InvMapfieldSearchObj.run().each(function (result) {
                let amtOverride = result.getValue(InvMapfieldSearchObj.columns[0])
                let supBillAddress = result.getValue(InvMapfieldSearchObj.columns[1])
                let pblConfig = result.getValue(InvMapfieldSearchObj.columns[2]) || null

                InvMapfieldLookUp.push({ amtOverride, supBillAddress, pblConfig })

                return true;
            });

            return InvMapfieldLookUp;

        } catch (Err) {
            log.error("Err@getMappingRecordData", Err)
            return [];
        }
    }

    function isEmpty(value) {
        let isEmptyObject = function (a) {
            if (typeof a.length === 'undefined') { // it's an Object, not an Array
                let hasNonempty = Object.keys(a).some(function nonEmpty(element) {
                    return !isEmpty(a[element]);
                });
                return hasNonempty ? false : isEmptyObject(Object.keys(a));
            }
            return !a.some(function nonEmpty(element) { // check if array is really not empty as JS thinks
                return !isEmpty(element); // at least one element should be non-empty
            });
        };
        return (
            value == false
            || typeof value === 'undefined'
            || value == null
            || (typeof value === 'object' && isEmptyObject(value))
        );
    }

    return {
        getInputData: getInputData,
        reduce: reduce,
        summarize: summarize
    }
});

Leave a comment

Your email address will not be published. Required fields are marked *