# PO Invoice Price Basis & Receipt Preflight (`winning_moonstone/po-invoice-price-basis-receipt-preflight`) Actor

Normalize per-piece, per-100 and per-1000 supplier prices; compare one invoice with PO lines, prior billed quantities and split receipts using exact decimal rules.

- **URL**: https://apify.com/winning_moonstone/po-invoice-price-basis-receipt-preflight.md
- **Developed by:** [月 明](https://apify.com/winning_moonstone) (community)
- **Categories:** Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$50.00 / 1,000 completed invoice preflight reports

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## PO Invoice Price Basis & Receipt Preflight

Compare one supplier invoice with one purchase order and cumulative split receipts. Normalize **per-piece, per-100, per-1000, or another explicit price basis** before comparing net unit prices. Supply structured JSON; receive an evidence-based exception report through the dataset and a downloadable JSON report.

For example, a PO quotes **USD 50 per 100 EA**, while the invoice says **USD 0.50 per EA**. Those prices agree. If the order is for 100 EA, earlier invoices billed 20 EA, the current invoice bills 40 EA, and two receipts contain 20 and 40 EA, the cumulative quantity also agrees. If the current invoice instead bills 50 EA, the report identifies a 10 EA receipt shortfall in the supplied scope.

Use it between an export or extraction step and human accounts-payable review. API users can put the same deterministic checks in n8n, Make, a spreadsheet workflow, or their own code. No LLM, paid external API, image/PDF download, fuzzy SKU match, or source-system connection is used.

### Input

Paste the following object as JSON text into `caseJson`. API input uses the same string field. Every identifier must be a string; leading zeros are preserved. Quantity units must match exactly, including letter case. Prices must already be **net of discounts and exclusive of tax and freight**.

```json
{
  "purchaseOrder": {
    "id": "PO-DEMO",
    "currency": "USD",
    "lines": [{
      "id": "001", "sku": "BOLT-001", "quantity": "100", "unit": "EA",
      "unitPrice": "50", "pricePerQuantity": "100", "previousInvoicedQuantity": "20"
    }]
  },
  "invoice": {
    "id": "INV-DEMO",
    "currency": "USD",
    "lines": [{
      "id": "1", "poLineId": "001", "sku": "BOLT-001", "quantity": "40", "unit": "EA",
      "unitPrice": "0.50", "pricePerQuantity": "1", "lineAmount": "20.00"
    }]
  },
  "receipts": [
    {"receiptId": "REC-A", "lineId": "1", "poLineId": "001", "quantity": "20", "unit": "EA"},
    {"receiptId": "REC-B", "lineId": "1", "poLineId": "001", "quantity": "40", "unit": "EA"}
  ],
  "receiptsComplete": true,
  "priorInvoicesComplete": true
}
```

`pricePerQuantity` defaults to 1 when omitted. `previousInvoicedQuantity` defaults to 0; confirm that zero is the full prior history before setting `priorInvoicesComplete=true`. Optional `lineAmount` is the declared net line amount. Optional `sku` is checked when supplied on both sides. All other displayed fields are required, except the receipt array and coverage flags. Unsupported fields are rejected, so tax, freight or discount values cannot be silently ignored.

Set `receiptsComplete=true` only when receipts cover the same cumulative PO-line scope as prior and current invoices. Missing flags produce `INCOMPLETE_REVIEW`. An explicitly confirmed empty receipt array means nothing has been received. Duplicate receipt keys or mismatched units make affected cumulative checks uncheckable instead of guessing a total.

### Checks and output

- Net unit-price differences after dividing by each explicit price basis.
- Current plus previous invoiced quantities beyond the ordered quantity.
- Cumulative invoice quantities beyond confirmed received quantities.
- Excess receipts, duplicate PO/invoice/receipt line keys and missing references.
- Currency, quantity-unit and supplied SKU mismatches.
- Declared net amounts versus `quantity × unitPrice / pricePerQuantity`.

The dataset contains **one report per run**, with findings, invoice-line checks, per-PO-line quantities and decimal totals serialized as strings. `REPORT` contains the same full report in the key-value store. `OUTPUT` contains processing and budget status. No report is written when the report charge cannot fit the budget.

Statuses:

| Status | Meaning |
| --- | --- |
| `MATCHED_WITHIN_CHECKED_RULES` | Supplied evidence and supported rules produced no discrepancies; this is not payment approval. |
| `REVIEW_FINDINGS` | Coverage is confirmed but discrepancies need human review. |
| `INCOMPLETE_REVIEW` | Missing scope, ambiguous references, incompatible units or currencies prevent a complete comparison. |

`paymentApproved` is always false. A completed report may contain errors or incomplete coverage; those are useful findings, not an Actor execution failure.

### Tolerances

`priceTolerancePercent` defaults to 0 and flags absolute deviations on either side of the PO price. `quantityTolerance` defaults to 0 and applies in each line's exact quantity unit. `lineAmountTolerance` defaults to 0.01 in the invoice currency; adjust it to your currency and rounding policy. Monetary arithmetic uses Decimal, with price-tolerance boundaries checked using cross-products. There is no inferred exchange rate, unit conversion or currency rounding rule.

### Pricing

**$0.05 per completed report ($50 per 1,000 reports)**, event `report`. Each report supports up to 500 PO lines, 500 invoice lines and 1,000 receipt lines, within 1 MiB of JSON text. Individual findings and lines are not separate billing events. Malformed inputs rejected before a report is generated are not billed as report events. Each repeated successful run is a separate completed report. The live Apify pricing configuration is authoritative.

### Data handling and scope

Input is processed on Apify and stored under its normal run-input controls. Reports remain in the user's private run dataset/key-value store. The code makes no external data calls and does not log input documents, line identifiers or report contents. Provide only authorized data; omit banking details, names, addresses and unnecessary fields. Use your Apify account controls to delete run/storage records.

This version supports one PO and one current invoice, plus explicit previously billed quantities and multiple receipts. It does not allocate specific receipt lots to invoice lines or check historical invoice IDs for duplicate payments. It does not read PDFs, support credit notes/returns, verify documents, calculate taxes/freight/discounts, screen fraud, connect to an ERP or approve payment. Exact supplied PO-line IDs are required; these limits are deliberate so missing evidence is visible.

### Rule background

Product rules were checked on **October 9, 2026** against these primary references. This Actor is an independent deterministic preflight and does not reproduce an ERP's complete policy engine:

- [Microsoft: Accounts payable invoice matching overview](https://learn.microsoft.com/en-us/dynamics365/finance/accounts-payable/accounts-payable-invoice-matching)
- [Microsoft: Invoice matching validation and tolerances](https://learn.microsoft.com/en-us/dynamics365/finance/accounts-payable/tasks/set-up-accounts-payable-invoice-matching-validation)
- [Oracle: Two-, three- and four-way approval](https://docs.oracle.com/cd/E26401_01/doc.122/e48760/T295436T366808.htm)

For support, use the Actor Issues tab with a small synthetic or redacted case and the ruleset version. Never submit credentials or unredacted private records in a public issue.

# Actor input Schema

## `caseJson` (type: `string`):

One JSON object with purchaseOrder, invoice, receipts, receiptsComplete and priorInvoicesComplete. Use net prices and exact string line IDs. See README for schema. Maximum 1 MiB UTF-8.

## `priceTolerancePercent` (type: `string`):

Non-negative plain decimal from 0 to 100. Absolute price deviations on either side beyond this percentage are reported.

## `quantityTolerance` (type: `string`):

Non-negative absolute tolerance in each line quantity unit. Default zero. Units must match exactly.

## `lineAmountTolerance` (type: `string`):

Absolute tolerance in the invoice currency for declared amount versus calculated net amount. No currency rounding rule is inferred.

## Actor input object example

```json
{
  "caseJson": "{\n  \"purchaseOrder\": {\n    \"id\": \"PO-DEMO\",\n    \"currency\": \"USD\",\n    \"lines\": [\n      {\n        \"id\": \"001\",\n        \"sku\": \"BOLT-001\",\n        \"quantity\": \"100\",\n        \"unit\": \"EA\",\n        \"unitPrice\": \"50\",\n        \"pricePerQuantity\": \"100\",\n        \"previousInvoicedQuantity\": \"20\"\n      }\n    ]\n  },\n  \"invoice\": {\n    \"id\": \"INV-DEMO\",\n    \"currency\": \"USD\",\n    \"lines\": [\n      {\n        \"id\": \"1\",\n        \"poLineId\": \"001\",\n        \"sku\": \"BOLT-001\",\n        \"quantity\": \"40\",\n        \"unit\": \"EA\",\n        \"unitPrice\": \"0.50\",\n        \"pricePerQuantity\": \"1\",\n        \"lineAmount\": \"20.00\"\n      }\n    ]\n  },\n  \"receipts\": [\n    {\n      \"receiptId\": \"REC-A\",\n      \"lineId\": \"1\",\n      \"poLineId\": \"001\",\n      \"quantity\": \"20\",\n      \"unit\": \"EA\"\n    },\n    {\n      \"receiptId\": \"REC-B\",\n      \"lineId\": \"1\",\n      \"poLineId\": \"001\",\n      \"quantity\": \"40\",\n      \"unit\": \"EA\"\n    }\n  ],\n  \"receiptsComplete\": true,\n  \"priorInvoicesComplete\": true\n}",
  "priceTolerancePercent": "0",
  "quantityTolerance": "0",
  "lineAmountTolerance": "0.01"
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("winning_moonstone/po-invoice-price-basis-receipt-preflight").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("winning_moonstone/po-invoice-price-basis-receipt-preflight").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call winning_moonstone/po-invoice-price-basis-receipt-preflight --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,winning_moonstone/po-invoice-price-basis-receipt-preflight"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/tsiqZjKPfW8Qo8ESN/builds/tbl2m2tY9MmOpv7P8/openapi.json
