Map reduce script to update document number of historical transactions.

To update document number of historical transactions,

  • First, we need to check the “Allow Override” checkbox in the set up auto generated number page

define(['N/record', 'N/search', 'N/format'],
    function (record, search, format) {
        "use strict";
        const TYPE_TRANSFER_ORDER = 48;
        const TYPE_INV_ADJUSTMENT = 11;
        const TYPE_VEND_RETURN_AUTH = 43;
        const TYPE_VEND_PAYMENT = 18;
        const TYPE_VEND_PREPAYMENT = 79;
        const TYPE_VEND_PREPAYMENT_APP = 80;
        const TYPE_INVOICE = 7;
        const TYPE_BILL = 17;
        const TYPE_PAYMENT = 9;
        const TYPE_DEPOSIT = 4;
        /**
         * Generates a document number based on the record details, transaction type, fiscal year, and prefix values.
         * Updates the relevant fields and locks/unlocks child records as required.
         *
         * @param {Object} newRecord - The NetSuite record object for which the document number is generated.
         * @param {string} transactionType - The type of transaction (e.g., 'check', 'inboundshipment', 'vendorprepaymentapplication').
         */
        function generateDocumentNumber(transactionType, tranDate, subsidiary) {
            try {
                let fiscalYear = getFiscalYearNew(tranDate);
                let parentRecord = fetchParentRecord(transactionType);
                let childRecord;
                if (parentRecord && parentRecord.internalId) {
                    childRecord = fetchChildRecord(subsidiary, parentRecord.internalId, fiscalYear);
                } else {
                    return {};
                }
                let fieldValues;
                if ((childRecord.isLocked) == false) {
                    fieldValues = { 'custrecord_jj_sub_rec_locked': true };
                    submitFieldsToRecord('customrecord_jj_sub_doc_number_config', childRecord.internalId, fieldValues);
                    fieldValues = { 'custrecord_jj_total_rec_count': Number(parentRecord.numberOfRecords) + 1 };
                    submitFieldsToRecord('customrecord_jj_document_number_config', parentRecord.internalId, fieldValues)
                    let documentNumber = (parentRecord.prefixValue) + '-' + (childRecord.prefixValue) + '-' + ((fiscalYear).toString().slice(-2)) + '-' + padNumber(Number(childRecord.sequenceNumber) + 1, 5);
                    fieldValues = { 'custrecord_jj_seq_numbers': padNumber(Number(childRecord.sequenceNumber) + 1, 5), 'custrecord_jj_sub_rec_locked': false };
                    submitFieldsToRecord('customrecord_jj_sub_doc_number_config', childRecord.internalId, fieldValues)
                    return documentNumber;
                }
            } catch (e) {
                log.error('Error @generateDocumentNumber', e);
                return '';
            }
        }
        /**
         * Updates the specified fields of a record with the given values.
         *
         * @param {string} transactionType - The type of the record (e.g., sales order, invoice).
         * @param {number} recordId - The internal ID of the record to update.
         * @param {Array<string>} fieldIds - An array of field IDs to update.
         * @param {Object} values - An object containing the field values to set.
         */
        function submitFieldsToRecord(recordType, recordId, fieldValues) {
            try {
                record.submitFields({
                    type: recordType,
                    id: recordId,
                    values: fieldValues,
                    options: {
                        enablesourcing: true,
                        ignoreMandatoryFields: true
                    }
                });
            } catch (e) {
                log.error('Error @submitFieldsToRecord', e);
            }
        }
        /**
         * Retrieves the last two digits of the fiscal year from a given transaction date.
         *
         * @param {string} tranDate - The transaction date in string format.
         * @returns {string|null} - The last two digits of the fiscal year, or null if an error occurs.
         */
        function getFiscalYearNew(tranDate) {
            try {
                let dateOBJ = format.parse({
                    value: tranDate,
                    type: format.Type.DATE
                });
                let year = dateOBJ.getFullYear();
                return year;
            } catch (e) {
                log.error('Error @getFiscalYearNew', e);
                return '';
            }
        }
        /**
         * Retrieves the corresponding transaction type ID based on the given transaction type.
         *
         * @param {string} transactionType - The type of transaction (e.g., 'transferorder', 'check').
         * @returns {number} - The transaction type ID, or null if the transaction type is not recognized.
         */
        function getTransactionTypeId(transactionType) {
            try {
                switch (transactionType) {
                    case 'transferorder':
                        return TYPE_TRANSFER_ORDER;
                    case 'inventoryadjustment':
                        return TYPE_INV_ADJUSTMENT;
                    case 'vendorreturnauthorization':
                        return TYPE_VEND_RETURN_AUTH;
                    case 'vendorpayment':
                        return TYPE_VEND_PAYMENT;
                    case 'vendorprepaymentapplication':
                        return TYPE_VEND_PREPAYMENT_APP;
                    case 'vendorprepayment':
                        return TYPE_VEND_PREPAYMENT;
                    case 'invoice':
                        return TYPE_INVOICE;
                    case 'vendorbill':
                        return TYPE_BILL;
                    case 'customerpayment':
                        return TYPE_PAYMENT;
                    case 'deposit':
                        return TYPE_DEPOSIT;
                    default:
                        return null;
                }
            } catch (e) {
                log.error('Error @ getTransactionTypeId', e);
                return '';
            }
        }
        /**
         * Fetches the parent record based on the transaction type and optional payment type.
         *
         * @param {string} transactionType - The type of the transaction to filter records.
         * @param {string} [paymentType] - (Optional) The payment type to refine the filter further.
         * @returns {Object|null} - An object containing record details, or null if no records are found or an error occurs.
         */
        function fetchParentRecord(transactionType) {
            try {
                let transactionTypeId = getTransactionTypeId(transactionType);
                if (!transactionTypeId) {
                    return null;
                }
                let filters = [
                    ["custrecord_jj_transaction_type", "anyof", transactionTypeId]
                ];
                let parentSearch = search.create({
                    type: 'customrecord_jj_document_number_config',
                    filters: filters,
                    columns: [
                        search.createColumn({ name: "internalid" }),
                        search.createColumn({ name: "custrecord_jj_transaction_type" }),
                        search.createColumn({ name: "custrecord_jj_doc_prefix" }),
                        search.createColumn({ name: "custrecord_jj_total_rec_count" })
                    ]
                });
                let pagedData = parentSearch.runPaged({ pageSize: 1000 });
                let resultObject = null;
                outerLoop:
                for (let i = 0; i < pagedData.pageRanges.length; i++) {
                    let page = pagedData.fetch({ index: i });
                    for (let j = 0; j < page.data.length; j++) {
                        let result = page.data[j];
                        resultObject = {
                            internalId: result.getValue({ name: "internalid" }),
                            transactionType: result.getValue({ name: "custrecord_jj_transaction_type" }),
                            prefixValue: result.getValue({ name: "custrecord_jj_doc_prefix" }),
                            numberOfRecords: result.getValue({ name: "custrecord_jj_total_rec_count" }) || 0
                        };
                        break outerLoop;
                    }
                }
                return resultObject || {};
            } catch (e) {
                log.error('Error @ fetchParentRecord', e);
                return {};
            }
        }
        /**
         * Fetches the child record based on the given subsidiary and parent record ID.
         *
         * @param {string} subsidiary - The subsidiary ID to filter child records.
         * @param {string} parentRecordId - The internal ID of the parent record to filter child records.
         * @returns {Object|null} - An object containing child record details, or null if no records are found or an error occurs.
         */
        function fetchChildRecord(subsidiary, parentRecordId, fiscalYear) {
            try {
                let childSearch = search.create({
                    type: 'customrecord_jj_sub_doc_number_config',
                    filters: [
                        ["custrecord_jj_parent_doc_config", "anyof", parentRecordId],
                        "AND",
                        ["custrecord_jj_doc_subsidiary", "anyof", subsidiary]
                    ],
                    columns: [
                        search.createColumn({ name: "internalid" }),
                        search.createColumn({ name: "custrecord_jj_doc_subsidiary" }),
                        search.createColumn({ name: "custrecord_jj_sub_code" }),
                        search.createColumn({ name: "custrecord_jj_parent_doc_config" }),
                        search.createColumn({ name: "custrecord_jj_sub_rec_locked" }),
                        search.createColumn({ name: "custrecord_jj_seq_numbers" }),
                        search.createColumn({ name: "custrecord_jj_doc_fiscal_year" })
                    ]
                });
                let pagedData = childSearch.runPaged({ pageSize: 1000 });
                let matchedRecord = null;
                let fallbackPrefix = null;
                outerLoop:
                for (let i = 0; i < pagedData.pageRanges.length; i++) {
                    let page = pagedData.fetch({ index: i });
                    for (let j = 0; j < page.data.length; j++) {
                        let record = page.data[j];
                        let fiscalYearInResult = record.getValue({ name: "custrecord_jj_doc_fiscal_year" });
                        if (!fallbackPrefix) {
                            fallbackPrefix = record.getValue({ name: "custrecord_jj_sub_code" });
                        }
                        if (fiscalYearInResult === String(fiscalYear)) {
                            matchedRecord = record;
                            break outerLoop;
                        }
                    }
                }
                if (matchedRecord) {
                    return {
                        internalId: matchedRecord.getValue({ name: "internalid" }),
                        subsidiary: matchedRecord.getValue({ name: "custrecord_jj_doc_subsidiary" }),
                        prefixValue: matchedRecord.getValue({ name: "custrecord_jj_sub_code" }),
                        parentRecord: matchedRecord.getValue({ name: "custrecord_jj_parent_doc_config" }),
                        isLocked: matchedRecord.getValue({ name: "custrecord_jj_sub_rec_locked" }),
                        sequenceNumber: matchedRecord.getValue({ name: "custrecord_jj_seq_numbers" }),
                        fiscalYear: matchedRecord.getValue({ name: "custrecord_jj_doc_fiscal_year" })
                    };
                }
                return {};
            } catch (e) {
                log.error('Error @ fetchChildRecord', e);
                return {};
            }
        }
        /**
                 * Pads a given number with leading zeros to match the specified length.
                 *
                 * @param {number} number - The number to be padded.
                 * @param {number} length - The desired total length of the output string.
                 * @returns {string} - The padded number as a string, or an empty string if an error occurs.
                 */
        function padNumber(number, length) {
            try {
                return String(number).padStart(length, '0');
            } catch (e) {
                log.error("error @ padNumber", e);
                return '';
            }
        }
        /**
        * Retrieves input data (transactions to process) for the Map/Reduce script.
        * Fetches transactions created on or after January 1, 2025, for multiple record types.
        * 
        * @returns {Object} - The search result set containing transaction records to be processed.
        */
        function getInputData() {
            try {
                let filters = [
                    ["type", "anyof", "TrnfrOrd"],
                    "AND",
                    ["trandate", "onorafter", "01-Jan-2025"],
                    "AND",
                    ["mainline", "is", "T"],
                    "AND",
                    ["custbody_jj_doc_num_updated", "is", "F"]
                ];
                let transactionSearch = search.create({
                    type: "transaction",
                    settings: [
                        { name: "consolidationtype", value: "ACCTTYPE" },
                        { name: "includeperiodendtransactions", value: "F" }
                    ],
                    filters: filters,
                    columns: [
                        search.createColumn({ name: "subsidiary", label: "Subsidiary" }),
                        search.createColumn({ name: "type", label: "Type" }),
                        search.createColumn({ name: "trandate", label: "Date" }),
                        search.createColumn({ name: "internalid", label: "Internal ID" })
                    ]
                });
                let pagedData = transactionSearch.runPaged({ pageSize: 1000 });
                log.debug("count", pagedData);
                let allResults = [];
                pagedData.pageRanges.forEach(function (pageRange) {
                    let page = pagedData.fetch({ index: pageRange.index });
                    page.data.forEach(function (result) {
                        allResults.push(result);
                    });
                });
                return allResults;
            } catch (e) {
                log.error('Error @getInputData', e);
                return [];
            }
        }
        /**
         * map - Processes each transaction record to generate and set a new document number (`tranid`) for each transaction.
         * 
         * @param {Object} context - The context object that contains the data for the current iteration.
         * @param {string} context.value - The transaction data to process.
         */
        function map(context) {
            try {
                let searchResult = JSON.parse(context.value);
                let recordId = searchResult.id;
                let transactionType = searchResult.recordType;
                let subsidiary = searchResult.values.subsidiary?.[0]?.value;
                let tranDate = searchResult.values.trandate;
                log.debug("recordId", recordId);
                log.debug("transactionType", transactionType);
                log.debug("subsidiary", subsidiary);
                log.debug("tranDate", tranDate);
                let documentNumber;
                switch (transactionType) {
                    case 'transferorder':
                    case 'inventoryadjustment':
                    case 'vendorreturnauthorization':
                    case 'vendorpayment':
                    case 'vendorprepayment':
                    case 'vendorprepaymentapplication':
                    case 'vendorbill':
                    case 'deposit':
                    case 'invoice':
                    case 'customerpayment':
                        documentNumber = generateDocumentNumber(transactionType, tranDate, subsidiary);
                        break;
                    default:
                        break;
                }
                log.debug("documentNumber", documentNumber)
                let fieldValues;
                if (documentNumber) {
                    fieldValues = { 'tranid': documentNumber,
                        'custbody_jj_doc_num_updated': true
                     };
                    submitFieldsToRecord(transactionType, recordId, fieldValues);
                }
            } catch (e) {
                log.error('Error in map function', e);
            }
        }
        /**
         * @description Summarize function for handling errors, and summarizing the execution.
         * @param {Object} summary - Summary object for MapReduce script execution
         */
        function summarize(summary) {
            try {
                log.audit({
                    title: 'MapReduce Script Summary',
                    details: `Processed ${summary.successCount} records, and encountered ${summary.errorCount} errors.`
                });
            } catch (e) {
                log.error('Error @summarize', e);
            }
        }
        return {
            getInputData: getInputData,
            map: map,
            summarize: summarize
        };
    });

Leave a comment

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