# AFE Approval Workflow (Purchasing)

## 1. Overview

**AFE** stands for **Authorization for Expenditure**. It is a formal document the office
prepares to justify and get sign-off on a Requisition (RQ) that is either out of the
ordinary or exceeds the available budget. The AFE is typically sent to the site owner,
signed (or approved by email), and returned to the office before the RQ can continue.

This release extends the existing AFE handling with a full approval workflow inside
ZeeBORN Purchasing.

**What changed compared to before:**

- Previously, an AFE was only required when the RQ had the **Extraordinary Expenses**
  flag set.
- From this release on, an AFE is **also required when the RQ pushes its cost account
  over the budget limit for the current period** (the "red pie chart" under the RQ
  header).
- The AFE approval itself now has a dedicated dialog (**Manage Approval**) with status,
  file attachment, remark, audit log and a controlled way to raise the budget limit.
- The RQ approval is now blocked in a defined sequence: fill out AFE &#8594; attach signed
  AFE / approval email &#8594; mark as approved &#8594; optionally raise the budget &#8594; approve the
  RQ itself.
- A new right **14105112 "Order without AFE Approval (Management Override)"** lets
  management roles skip the AFE block if needed.

## 2. Prerequisites / Setup

Before using the workflow, make sure the following is in place:

- **Right 14105112** ("Order without AFE Approval (Management Override)") is granted to
  the management roles that are allowed to bypass the AFE.
- The following configuration entries are set:
  - **UseAuthorizationForExpenditure** &#8594; enables the feature as a whole.
  - **UseAuthorizationForExpenditureFormMandatory** &#8594; makes the AFE mandatory when the
    triggers (see section 3) apply.
  - **UseAuthorizationForExpenditureFormID** &#8594; the ID of the AFE form template that
    the user will fill out.
- The database migration in `docs/sql/2026-04-19_task4773_afe.sql` has been applied.
  It is already on **ALBERICH** and **victoriademo**; **bluesquared** is updated
  through the regular setup pipeline.
- For each site that can trigger an AFE, `lm_sites.OwnerContactID` must point to a
  contact in `om_contacts`, and that contact must have a business email entry in
  `om_communication`. Without this, the **Send by Mail** step cannot pre-fill a
  recipient.

## 3. When is an AFE required?

An AFE is required on an RQ if **either** of the following is true:

1. **Extraordinary Expenses** is checked on the RQ.
2. The RQ would push the cost account over the budget limit in the current period
   (the pie chart under the RQ header turns **red**).

In the RQ view, directly under the pie chart, an info block is shown in these cases:

- **"AFE required (extraordinary expenses)"**
- **"AFE required (budget overrun)"**

If neither trigger applies, no AFE is needed and the RQ can be approved as before.

## 4. Filling out the AFE form

The AFE form itself is the existing **Expenses Form** feature &#8212; it has not changed.

In the RQ, click the **Fill Out** button next to the AFE info block. A form based on
the template configured in **UseAuthorizationForExpenditureFormID** opens. Fill in the
questions as required and save.

For details on how to work with Expenses Forms, please refer to the existing
**ZeeBORN Expenses Forms** documentation.

Once an AFE form has been filled out for the RQ, the approval workflow in the next
section becomes available.

## 5. The AFE Approval dialog (Manage Approval)

The **Manage Approval** dialog is the central place to handle the AFE sign-off.

**How to open it:**

In the RQ view, click **Manage Approval** next to the AFE block. The dialog
**Purchase.AFEApproval** opens.

**Fields and areas:**

- **Status** &#8211; shows the current approval status:
  - **Pending** &#8211; AFE filled out but not yet signed off.
  - **Approved** &#8211; signed AFE / approval mail attached and approver marked it done.
  - **Rejected** &#8211; AFE was explicitly rejected.
- **Attached file** &#8211; the signed AFE PDF or the owner's approval email exported as
  PDF. Required before you can use **Approve**.
- **Remark** &#8211; free text, up to 500 characters. Use this for context the auditor or
  the next reviewer should see.
- **Approver / Timestamp** &#8211; automatically filled when **Approve** or **Reject** is
  used. Read-only.

**Buttons:**

- **Approve**
- **Reject**
- **Reset to Pending**
- **Increase Budget**
- **Open attached file**
- **Export AFE**
- **Send by Mail**

The individual actions are described in the following sections.

## 6. Attaching the signed AFE / approval email

Before the AFE can be set to **Approved**, a file must be attached. Accepted content:

- The signed AFE PDF returned by the owner, or
- The owner's approval email saved/exported as PDF.

Click the attach button in the dialog, pick the file, and it is stored with the AFE.
Use **Open attached file** at any time to verify what was attached.

If **Approve** is clicked without an attachment, ZeeBORN blocks the action and asks
for the file first.

## 7. Approving, Rejecting, Resetting to Pending

- **Approve** &#8211; sets the AFE status to **Approved**. Requires an attached file.
  Stores the current user as approver and the current timestamp. Only now the RQ
  approval block due to the AFE is lifted (the budget check still applies, see
  section 10).
- **Reject** &#8211; sets the status to **Rejected**. The RQ cannot be approved through
  the normal path while the AFE is rejected. Use the remark to state why.
- **Reset to Pending** &#8211; sets the status back to **Pending**. The existing file,
  remark, approver name and timestamp are **kept on purpose** so the audit trail stays
  complete. If the attached file really must be removed, an administrator can delete
  it separately. **Reset to Pending** is only visible and usable for users who have
  the Management Override right **14105112**.

## 8. Exporting the AFE form

Use **Export AFE** to save the filled-out AFE form to a file on your machine. The
file is typically a PDF based on the AFE template.

Use this when you want to:

- Send the AFE to the owner through a channel other than the built-in Outlook draft.
- Keep a local copy before sending.
- Attach the AFE to an external system.

The export does not change any status on the RQ.

## 9. Sending the AFE to the owner by email

**Send by Mail** creates a new **Outlook draft** for the AFE, pre-filled as follows:

- **To:** the business email of the contact set in
  `lm_sites.OwnerContactID` for the RQ's site (looked up through `om_communication`).
- **Subject / Body:** tabular layout in ZeeBORN branding, summarising the RQ and the
  AFE reason.
- **Attachment:** the exported AFE file.

The draft opens in Outlook so the user can review, edit the text, add CCs, and
**hit Send manually**. ZeeBORN does not send the mail for you.

**Sent-to-Owner indicator** in the RQ view:

- Before **Send by Mail** has been used, a **red "Not yet sent to Owner"** box is
  shown.
- After **Send by Mail** has been used, the box turns **turquoise** and reads
  **"Sent to Owner on TT.MM.JJJJ HH:MM"**. The timestamp is stored in
  `pu_expensesforms.SentToOwnerAt`.

Note: the indicator tracks the moment the draft was generated in ZeeBORN. It does
not verify that the user actually clicked **Send** in Outlook.

## 10. Increasing the budget limit

If the AFE is triggered by a budget overrun, approving the AFE alone does **not**
raise the budget. The limit on the cost account is still exceeded, and the RQ
approval is still blocked with:

> Budget is still exceeded by this RQ. Please increase the budget limit in the
> Manage Approval dialog before proceeding.

To raise the limit, click **Increase Budget** in the Manage Approval dialog. A small
form opens with:

- **New value** &#8211; the new budget limit for this cost account / period.
- **Reason** &#8211; **mandatory** free-text reason.
- **AFE file** &#8211; optional, pre-filled with the attached AFE if available.
- **RQ reference** &#8211; optional, pre-filled with the current RQ.

On confirm, ZeeBORN calls `UpdateBudgetLimit`, which:

1. Updates `bu_limits.Value` to the new limit.
2. Writes an entry to `bu_limits_changes` containing the old value, the new value,
   the reason, the user, the timestamp, and the AFE / RQ references.

From this point on, the pie chart uses the new limit, and &#8211; if the RQ now fits &#8211;
the RQ approval is no longer blocked by the budget check.

## 11. Management override (right 14105112)

Users with the right **14105112 "Order without AFE Approval (Management Override)"**
can approve the RQ even if:

- No AFE has been filled out for the required reason,
- The AFE is still pending or rejected, or
- The budget is still exceeded.

The block messages from section 12 are still shown, but these users get an extra
confirmation and can proceed. Use this right sparingly &#8211; every override is visible
in the RQ approval history.

Users with this right are also the only ones who can use **Reset to Pending**
(see section 7).

## 12. RQ approval block messages

When the user tries to approve the RQ, ZeeBORN checks the AFE state in sequence and
can block with one of these messages:

- **"Please fill out AFE form (REASON) first."** &#8211; AFE is required but has not been
  filled out yet. REASON is either **extraordinary expenses** or **budget overrun**.
- **"AFE for REASON is not yet approved. Please attach the signed AFE / approval
  mail and mark as approved first."** &#8211; AFE is filled out, but the status in
  **Manage Approval** is not **Approved**.
- **"Budget is still exceeded by this RQ. Please increase the budget limit in the
  Manage Approval dialog before proceeding."** &#8211; AFE approved, but the cost account
  is still over the limit. Use **Increase Budget** (section 10).

Each message tells the user the exact next step. Users with right 14105112 can
override the block.

## 13. Approval history and audit trail

All changes relevant for audit are kept permanently:

- The AFE itself (filled-out answers) stays in `pu_expensesforms`.
- The attached signed AFE / approval email stays with the AFE.
- The approver name and timestamp stay on the AFE even after **Reset to Pending**.
- Every budget limit change is written to `bu_limits_changes`, with old value, new
  value, mandatory reason, user, timestamp, and references to the AFE and RQ that
  triggered the change.

Nothing in the audit log is deleted automatically. If a wrong entry must be
corrected, an administrator has to do it explicitly on the database.

## 14. Known limitations / notes

- **Send by Mail** creates an Outlook draft only. The user must click **Send** in
  Outlook. ZeeBORN does not verify that the mail was actually sent.
- The sent-to-owner timestamp reflects the draft generation, not the Outlook send.
- If `lm_sites.OwnerContactID` is missing or the contact has no business email in
  `om_communication`, the **To** field in the Outlook draft will be empty and the
  user has to fill it in manually.
- The AFE form template is controlled by the configuration
  **UseAuthorizationForExpenditureFormID**. Changing the template affects new AFEs
  only; existing AFEs keep the template they were filled out with.
- **Reset to Pending** intentionally keeps the attached file and the approver trail.
  This is by design for audit reasons and is not a bug.
- The Management Override right **14105112** bypasses the AFE block entirely. Grant
  it only to roles that are supposed to take that responsibility.

  # Accrual Report — "Also include POs already invoiced" option

## 1. Overview

The **Accrual Report** in ZeeBORN Purchasing (*Purchasing → Reports → Open POs / Provision*) computes the budget-side accrual by listing Purchase Orders that are **not yet completely invoiced**. The remaining (uninvoiced) part of each PO is what still has to be accrued against the budget for the selected period.

This release extends the report with an option for controlling use cases that need the period total — not just the remaining accrual. Specifically:

- A new option **"Also include POs already invoiced in this period (for controlling)"** in the Loading-Options dialog.
- A new grid column **"Already Invoiced"** that flags such rows.
- Two new controlling columns **"Budgeted"** and **"Invoiced"** (dynamic visibility).
- Two silent SQL bugs in the underlying query have been fixed as bycatch.

The report's default behaviour is unchanged. The new columns are only shown when the new option is active.

---

## 2. Running the report with the new option

1. Open *Purchasing → Reports → Provision / Open POs*.
2. In the **Loading Options** dialog, set the period (From / To) and the usual filters as before.
3. In the sub-group **Additional Options**, tick the checkbox **"Also include POs already invoiced in this period (for controlling)"**.
4. Click **Load**.

When the checkbox is **off** (default), the report behaves exactly as before — only open POs that still have an uninvoiced portion are listed.

When the checkbox is **on**, the report additionally includes POs whose status is one of:

- **Completely Paid**
- **Closed**
- **Closed Partially Paid**
- **Closed Completely Paid**

i.e. POs that were already fully invoiced within the selected period. These rows typically have an `OriginalPrice` of 0 (because nothing is left to accrue), but they carry the budgeted and invoiced amounts for controlling.

---

## 3. Reading the report

The grid shows one row per PO / budget position. The relevant columns for this feature:

- **Already Invoiced** — a flag column. Set for rows where the PO was already fully invoiced in the selected period. These are the rows added by the new option.
- **Budgeted** — the amount originally booked into the budget for this position (see section 4).
- **Invoiced** — the amount that was actually invoiced, in original currency (see section 4).

All other columns (PO No., Supplier, Account, OriginalPrice, etc.) are unchanged.

**When the new checkbox is off**, the **Already Invoiced** flag column is still present but always empty, and **Budgeted** / **Invoiced** stay hidden — the report looks exactly like before.

---

## 4. The two controlling columns "Budgeted" and "Invoiced"

For rows flagged **Already Invoiced**, the accrual itself is correctly 0 — there is nothing left to accrue. The two controlling columns provide the additional context needed to reconcile period totals:

- **Budgeted** — what was originally booked into the budget for this position (`OV_Budget + OV_AdditionalCosts`).
- **Invoiced** — what was actually invoiced against this position, in the PO's original currency.

**When these columns appear**

The columns are hidden by default. They become visible automatically when **both** of the following are true:

- the **"Also include POs already invoiced in this period"** checkbox is active, **and**
- the current report contains at least one already-invoiced row.

If either condition is not met, the columns stay hidden to avoid cluttering the standard accrual view.

**What they are for**

Controlling often needs the full period picture in a single list — the open accrual *plus* what has already been posted. With **Budgeted** and **Invoiced** visible, the same list can be used to reconcile period totals (budget position vs. actual invoiced amount) without switching to a separate report.

---

## 5. Excel export

The Excel export follows the same rules as the on-screen grid:

- **Already Invoiced** is always exported.
- **Budgeted** and **Invoiced** are exported **only** when they are visible in the grid (i.e. checkbox on and at least one already-invoiced row present).

No separate option is needed — what you see on screen is what you get in Excel.

---

## 6. When to use this option vs. the default

- **Leave the checkbox off** when you want the classic accrual figure — the amount still to be accrued against the budget for open POs. This is the standard month-end accrual use case.
- **Switch the checkbox on** when you need the period total from a controlling perspective — budgeted vs. invoiced for every PO that was active in the period, including the ones that were fully invoiced during the period. Use this for period reconciliation, not for calculating the accrual itself.

Rule of thumb: accrual → off, period controlling → on.

---

## 7. Known issues fixed in this release

Two pre-existing bugs in the Accrual Report's underlying query were fixed as part of this change:

- **POs with status "Partially Invoiced" or "Partially Paid" were silently dropped** from the report due to a missing comma in the state-filter list. These POs now appear correctly in the standard (checkbox-off) accrual.
- **An RfQ-text column was failing silently** due to a wrong SQL alias (`r.` where `p.` was meant). The column now shows the expected text.

Both fixes apply regardless of the new checkbox.

---

## 8. Known limitations / notes

- The **Invoiced** column shows the invoiced amount in the PO's **original currency**. If your report mixes currencies, do not sum this column across rows without conversion.
- **Budgeted** reflects the budget booking at the time the PO was placed (`OV_Budget + OV_AdditionalCosts`) and does not re-evaluate against later budget adjustments.
- The **Already Invoiced** flag is based on PO status at report-load time. Re-run the report if statuses have changed since.
- Already-invoiced rows typically carry `OriginalPrice = 0`. That is expected — the accrual has nothing left to post; the controlling value is in **Budgeted** / **Invoiced**.

*[PO]: Purchase Order  
*[AZA]: local folder
*[(PO]: Purchase Order  
*[RQ]: Requisition  
*[(RQ]: Requisition  
*[RfQ]: Request for Quotation  
*[(RfQ]: Request for Quotation  
*[SO]: Service Order  
*[(SO]: Service Order  
*[SQ]: Service Requisition  
*[(SQ]: Service Requisition  
*[SRfQ]: Service Request for Quotation  
*[OoB]: Open on Board  
*[(OoB]: Open on Board 
*[ART]: Average Running Time (operation hours per day) 
*[MC]: Master Contract
*[ADS]: Advantage Database Server (database engine used in previous versions of the ZeeBORN software)
*[.replic]: file extension for replication files (aka data transfer files)
