We noticed that when the A/P is locked, Vendor Bil...
# general
l
We noticed that when the A/P is locked, Vendor Bills approved still post to the A/P in the locked period because the approval WF is set to Execute as Admin. Aside from changing the business process, how can we prevent this issue? We need the Execute as Admin option for the other approval WF stuff because the user roles don’t have access to other records included in the WF. Is our option only to give these roles access to everything used in the approval WF?
d
could have the WF check if the AP is locked before approving (then return user error or change the posting period before approving?). it's a pity that, unlike scripts, you can't select a specific role to execute with (otherwise you could set up a custom admin role, just without the 'post to closed period' permission)
m
We had this same issue, except that our approvals use a script instead of a workflow. We ended up adding a check in the script for locked periods and updating the period accordingly. I don't think a workflow would be able to do this
Copy code
/**
 * Calculate the accounting period that should be used on the approved supplier invoice, respecting
 * the per subsidiary period lock status. This is needed because this script is executed under
 * Administrator role which has "Override Period Restrictions" permission, and could potentially
 * approve invoices in a locked period if not for this check.
 *
 * See CORP-4222 for further details.
 *
 * Code is loosely based on <https://netsuite.custhelp.com/app/answers/detail/a_id/97841/>
 *
 * @param {nlobjRecord} record Invoice Record
 * @returns {string} Internal ID of the period. This will be the existing period if unlocked,
 *   otherwise the next unlocked period
 */
APApprovalSuitelet.getPostingPeriod = function (record) {
  var subsidiary = record.getFieldValue("subsidiary");
  var postingPeriod = record.getFieldValue("postingperiod");

  if (!this.isPeriodAPLockedForSubsidiary(postingPeriod, subsidiary)) {
    return postingPeriod;
  }

  var futurePeriods = this.getFutureAccountingPeriods(postingPeriod);

  for (var i = 0; i < futurePeriods.length; i++) {
    if (!this.isPeriodAPLockedForSubsidiary(futurePeriods[i], subsidiary)) {
      return futurePeriods[i];
    }
  }

  throw nlapiCreateError(
    "ERROR_CALCULATING_POSTING_PERIOD",
    "Unable to find an unlocked posting period."
  );
};

/**
 * @param {string} postingPeriod Internal ID of Account Period to Check
 * @param {string} subsidiary Internal ID of subsidiary to check
 * @returns {boolean} True if period is locked for AP transactions for given subsidiary
 */
APApprovalSuitelet.isPeriodAPLockedForSubsidiary = function (postingPeriod, subsidiary) {
  var results =
    nlapiSearchRecord(
      "taskitemstatus",
      undefined,
      [
        ["itemtype", "anyof", "PCP_LOCK_AP"],
        "AND",
        ["subsidiary", "anyof", subsidiary],
        "AND",
        ["period", "anyof", postingPeriod],
      ],
      [new nlobjSearchColumn("complete")]
    ) || [];

  if (results.length !== 1) {
    return false;
  }

  return results[0].getValue("complete") === "T";
};

/**
 * @param {string} postingPeriod Internal ID of Account Period to check from
 * @returns {string[]} Array of Internal IDs for future accounting periods that are not fully locked
 *   for AP
 */
APApprovalSuitelet.getFutureAccountingPeriods = function (postingPeriod) {
  var results =
    nlapiSearchRecord(
      "accountingperiod",
      undefined,
      [
        ["internalidnumber", "greaterthan", postingPeriod],
        "AND",
        ["aplocked", "is", "F"],
        "AND",
        ["isquarter", "is", "F"],
        "AND",
        ["isadjust", "is", "F"],
        "AND",
        ["isyear", "is", "F"],
      ],
      [new nlobjSearchColumn("internalid").setSort(false)]
    ) || [];

  var accountPeriods = results.map(function (result) {
    return result.getId();
  });

  // result has duplicates for some reason, so remove them
  return accountPeriods.filter(function (item, pos) {
    return accountPeriods.indexOf(item) === pos;
  });
};
d
unfortunate if WF can't change the posting period. so would just have to return a user error, right? and also make sure to check if period is locked FIRST, otherwise you'll potentially have to undo any changes the WF makes during the approval process
@michoel is this script correct to assume that internalid would be sequential chronologically? I.e. would it be possible to create Jan'24 before Dec'23?
m
Yup, that would be technically possible. I was comfortable enough doing this for my own account where I know this is not the case
👍 1
unfortunate if WF can't change the posting period.
so would just have to return a user error, right? and also make sure to check if period is locked FIRST, otherwise you'll potentially have to undo any changes the WF makes during the approval process
I don't know how a workflow would even check the lock status (without a Workflow Action Script)
d
of course facepalm
l
Thank you both. I guess we'd really have to script it then.