Skip to workflow content
Vora
← Workflow library

Commercial Operations

Check a project quote against a margin floor

Review a project quote using exact minor-unit costs and an operator-selected margin floor. The output separates fee, cost, profit and the margin decision before someone issues the quote.

Experimental native example · local preview · revision 1

WORKFLOW PREVIEW

See how it works

2 nodes · 1 connections

Use + and − to zoom, F to fit. Press Enter on a node to inspect it.

Private preview. These tested drafts are available for inspection. Adding them to a Vora workspace is still being tested.

What you need

  • A fee and all included cost items expressed as integers in the same currency minor unit.
  • Unique cost item IDs and an approved margin floor in basis points; 3000 means 30%.
Follow the workflow step by step
  1. Supplied agency data

    Receive the explicitly supplied manual input packet.

  2. Check a project quote against a margin floor

    Return integer-minor-unit cost, profit, margin in basis points and a review/pass decision for a quote. Do not set prices or issue a quote.

Setup and review

  • Include the delivery and tool costs the quote owner intends to assess.
  • Test a profitable quote, one exactly at the margin floor and a loss-making quote.
  • Inspect the totals and decision, then obtain the normal commercial approval before issuing a quote.

Inspect every node

Expand a node to inspect its complete settings. Review the runtime and adoption evidence before installing it.

Supplied agency data · wfManualTrigger
{
  "label": "Supplied agency data"
}
Check a project quote against a margin floor · code
{
  "label": "Check a project quote against a margin floor",
  "code": "\nfunction obj(value, keys, required = keys) {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected object');\n  if (Object.keys(value).some(key => !keys.includes(key))) throw new Error('Unknown input field');\n  if (required.some(key => !Object.hasOwn(value, key))) throw new Error('Missing required input field');\n  return value;\n}\nfunction text(value, name, max = 200, allowEmpty = false) {\n  if (typeof value !== 'string' || value.length > max || (!allowEmpty && !value.trim())) throw new Error('Invalid ' + name);\n  return value.trim();\n}\nfunction list(value, name, max = 500) {\n  if (!Array.isArray(value) || value.length > max) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction int(value, name, min = 0, max = Number.MAX_SAFE_INTEGER) {\n  if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction amount(value, name) {\n  if (!Number.isFinite(value) || value < 0 || value > 1000000000) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction email(value) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value); }\nfunction day(value) {\n  if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) throw new Error('Invalid calendar date');\n  const ms = Date.parse(value + 'T00:00:00Z');\n  if (!Number.isFinite(ms) || new Date(ms).toISOString().slice(0, 10) !== value) throw new Error('Invalid calendar date');\n  return ms;\n}\nfunction instant(value) {\n  if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) throw new Error('Invalid UTC timestamp');\n  const ms = Date.parse(value);\n  const normalized = value.includes('.') ? value : value.replace('Z', '.000Z');\n  if (!Number.isFinite(ms) || new Date(ms).toISOString() !== normalized) throw new Error('Invalid UTC timestamp');\n  return ms;\n}\nfunction uniqueIds(items) {\n  const ids = items.map(item => text(item.id, 'item ID', 120));\n  if (new Set(ids).size !== ids.length) throw new Error('Duplicate item ID');\n  return ids;\n}\nfunction ratioBps(numerator, denominator) {\n  const n = BigInt(numerator) * 10000n;\n  const d = BigInt(denominator);\n  const quotient = n / d;\n  const floor = n < 0n && n % d !== 0n ? quotient - 1n : quotient;\n  const result = Number(floor);\n  if (!Number.isSafeInteger(result)) throw new Error('Unsafe calculated ratio');\n  return result;\n}\n\n\nconst x = obj(context.input, ['quoteId','currency','feeMinor','costItems','minimumMarginBps']);\nconst quoteId = text(x.quoteId, 'quote ID', 120);\nif (typeof x.currency !== 'string' || !/^[A-Z]{3}$/.test(x.currency)) throw new Error('Invalid currency');\nconst feeMinor = int(x.feeMinor, 'fee minor units', 1);\nconst minimum = int(x.minimumMarginBps, 'minimum margin', 0, 10000);\nconst costs = list(x.costItems, 'cost items', 100);\ncosts.forEach(item => obj(item, ['id','amountMinor']));\nuniqueIds(costs);\nlet totalCostMinor = 0;\nfor (const item of costs) { totalCostMinor += int(item.amountMinor, 'cost minor units'); int(totalCostMinor, 'total cost'); }\nconst profitMinor = feeMinor - totalCostMinor;\nconst marginBps = ratioBps(profitMinor, feeMinor);\nreturn {quoteId, currency:x.currency, totalCostMinor, profitMinor, marginBps, decision:marginBps >= minimum ? 'pass' : 'review'};\n",
  "outputVar": "result",
  "timeout": 1000,
  "memoryLimit": 16
}
Input and output contracts

Input

{
  "type": "object",
  "properties": {
    "quoteId": {
      "type": "string",
      "maxLength": 120
    },
    "currency": {
      "type": "string",
      "maxLength": 3
    },
    "feeMinor": {
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "costItems": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 120
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "id",
          "amountMinor"
        ],
        "additionalProperties": false
      },
      "maxItems": 100
    },
    "minimumMarginBps": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10000
    }
  },
  "required": [
    "quoteId",
    "currency",
    "feeMinor",
    "costItems",
    "minimumMarginBps"
  ],
  "additionalProperties": false
}

Output

{
  "type": "object",
  "properties": {
    "quoteId": {
      "type": "string",
      "maxLength": 120
    },
    "currency": {
      "type": "string",
      "maxLength": 3
    },
    "totalCostMinor": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "profitMinor": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "marginBps": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "decision": {
      "type": "string",
      "maxLength": 20
    }
  },
  "required": [
    "quoteId",
    "currency",
    "totalCostMinor",
    "profitMinor",
    "marginBps",
    "decision"
  ],
  "additionalProperties": false
}

Choose an output for this job

This business workflow also has 15 delivery formats. Compare their inputs, output examples and limits.

Compare implementations for this job

Example input and result

These examples use synthetic data.

Input

{
  "quoteId": "quote-01",
  "currency": "EUR",
  "feeMinor": 100000,
  "costItems": [
    {
      "id": "delivery",
      "amountMinor": 50000
    },
    {
      "id": "tools",
      "amountMinor": 10000
    }
  ],
  "minimumMarginBps": 3000
}

Expected result

{
  "quoteId": "quote-01",
  "currency": "EUR",
  "totalCostMinor": 60000,
  "profitMinor": 40000,
  "marginBps": 4000,
  "decision": "pass"
}

Success means: Return integer-minor-unit cost, profit, margin in basis points and a review/pass decision for a quote. Do not set prices or issue a quote.

When something goes wrong

  • Zero fee, fractional minor units, duplicate item IDs or unsafe arithmetic stop the calculation. Correct the quote data. A review result retains its negative profit and margin so the owner can revise scope, cost assumptions or fee.

Limits and costs

  • No foreign-exchange conversion, tax treatment, automatic pricing or quote issuance occurs. Currency is a three-letter label supplied by the operator. Missing costs can make the result misleading; reconcile the input scope.
  • No paid model calls or metered third-party operations are declared in this pure graph. Vora plan availability, compute and quotas still apply; pricing has not been verified by this batch.

USE IN VORA

Check a project quote against a margin floor

Review this workflow’s steps and required inputs. Get access to Vora before setting it up in your workspace.

Workspace installation is being connected. This page lets you inspect the workflow; it does not create or run one.

Required input fields
{
  "additionalProperties": false,
  "properties": {
    "costItems": {
      "items": {
        "additionalProperties": false,
        "properties": {
          "amountMinor": {
            "maximum": 9007199254740991,
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "maxLength": 120,
            "type": "string"
          }
        },
        "required": [
          "id",
          "amountMinor"
        ],
        "type": "object"
      },
      "maxItems": 100,
      "type": "array"
    },
    "currency": {
      "maxLength": 3,
      "type": "string"
    },
    "feeMinor": {
      "maximum": 9007199254740991,
      "minimum": 1,
      "type": "integer"
    },
    "minimumMarginBps": {
      "maximum": 10000,
      "minimum": 0,
      "type": "integer"
    },
    "quoteId": {
      "maxLength": 120,
      "type": "string"
    }
  },
  "required": [
    "quoteId",
    "currency",
    "feeMinor",
    "costItems",
    "minimumMarginBps"
  ],
  "type": "object"
}