---
title: "Purchase"
description: "Vendors and purchase orders. Keep vendors and the purchase orders you send them, with order lines and a draft to received flow."
canonical: "https://obop.dev/apps/purchase/"
lang: "en"
---

# Purchase

## What it is for

Keep vendors and the purchase orders you send them, with order lines and a draft to received flow. Totals are entered, not summed from the lines.

## What the package does and does not do

Vendors, purchase orders and order lines with a draft, sent, confirmed, received and cancelled flow. An order has a number from a series, its total is the sum of its lines, and receiving the goods creates a draft stock move per line at the receiving location (the warehouse validates them).

## Module details

- **Version:** `0.3.0`
- **Author:** SpaceCorps
- **Licence:** [AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later.html)
- **Needs core:** `^0.2` (core 0.2.0 is current)
- **Maturity:** Beta
- **Categories:** Supply chain

## Models

### `purchase_vendor`

Purchasing terms for a contact you buy from

Read group: `purchase.user`. Write group: `purchase.manager`.

| Field | Kind | Data class | Personal data | Group | Agents see | Description |
|---|---|---|---|---|---|---|
| `party_id` | ref → party | `none` | no | `purchase.user` | in clear | The contact record of the vendor |
| `vendor_reference` | text | `none` | no | `purchase.user` | in clear | Your customer number at this vendor |
| `payment_terms_days` | number | `none` | no | `purchase.user` | in clear | Days allowed to pay an invoice |
| `lead_time_days` | number | `none` | no | `purchase.user` | in clear | Usual days between order and delivery |
| `iban` | text | `financial` | yes | `purchase.manager` | withheld | Bank account the vendor is paid into |
| `active` | bool | `none` | no | `purchase.user` | in clear | Whether you still buy from this vendor |

### `purchase_order`

A request to a vendor to supply goods or services

Read group: `purchase.user`. Write group: `purchase.user`.

| Field | Kind | Data class | Personal data | Group | Agents see | Description |
|---|---|---|---|---|---|---|
| `number` | sequence | `none` | no | `purchase.user` | in clear | Purchase order number such as PO-00042, assigned when the order is created |
| `reference` | text | `none` | no | `purchase.user` | in clear | Order number printed on the document |
| `vendor_id` | ref → purchase_vendor | `none` | no | `purchase.user` | in clear | The vendor ordered from |
| `ordered_on` | date | `none` | no | `purchase.user` | in clear | Date the order was placed (YYYY-MM-DD) |
| `expected_on` | date | `none` | no | `purchase.user` | in clear | Date the goods are expected (YYYY-MM-DD) |
| `currency` | text | `none` | no | `purchase.user` | in clear | ISO 4217 currency code, for example EUR |
| `receive_location_id` | ref → inventory_location | `none` | no | `purchase.user` | in clear | Where the goods are to be received; receiving the order creates the stock moves to this location |
| `total` | money | `none` | no | `purchase.user` | in clear | Order total: the sum of the line amounts (minor currency units, e.g. cents) |
| `notes` | longtext | `none` | no | `purchase.user` | in clear | Delivery instructions and remarks for the vendor |
| `state` | select (draft, sent, confirmed, received, cancelled) | `none` | no | `purchase.user` | in clear | Order status: draft, sent, confirmed, received, cancelled |

### `purchase_order_line`

One product or service on a purchase order

Read group: `purchase.user`. Write group: `purchase.user`.

| Field | Kind | Data class | Personal data | Group | Agents see | Description |
|---|---|---|---|---|---|---|
| `order_id` | ref → purchase_order | `none` | no | `purchase.user` | in clear | The order the line belongs to |
| `product_id` | ref → product | `none` | no | `purchase.user` | in clear | The product ordered |
| `description` | text | `none` | no | `purchase.user` | in clear | Wording for the vendor if different from the product name |
| `quantity` | number | `none` | no | `purchase.user` | in clear | Units ordered |
| `unit_price` | money | `none` | no | `purchase.user` | in clear | Agreed price per unit (minor currency units, e.g. cents) |
| `amount` | money | `none` | no | `purchase.user` | in clear | Line amount: quantity times unit price (minor currency units) |
| `received_quantity` | number | `none` | no | `purchase.user` | in clear | Units received so far |

## Workflows

### `purchase_order`

| Action | Moves | Who may run it | Approval |
|---|---|---|---|
| `purchase_order.send` | state: draft → sent | `purchase.user` | no |
| `purchase_order.confirm` | state: sent → confirmed | `purchase.manager` | yes |
| `purchase_order.receive` | state: confirmed → received | `purchase.user` | no |
| `purchase_order.cancel` | state: draft → cancelled | `purchase.user` | no |

## Permission groups

Groups used by this app:

- `purchase.manager`
- `purchase.user`

## Approval steps

Steps an agent can only propose:

- `purchase_order.confirm`

## What agents can and cannot do

An agent can read, search, create and update records of this app, and run the actions that need no approval, within the permission groups of its token.

It can only propose these steps, and a person must approve them: `purchase_order.confirm`, `purchase_vendor.delete`, `purchase_order.delete`, `purchase_order_line.delete`.

These fields are withheld from agents entirely: `purchase_vendor.iban`.

## Dependencies

Each dependency states the versions it accepts. Built-in apps version with the core, so a requirement on one is checked against the core version.

- [Contacts](/apps/contacts/): `^0.2` (built in)
- [Inventory](/apps/inventory/): `>=0.3, <1`
- [Products](/apps/products/): `^0.2` (built in)

Installing this app from the marketplace also installs the dependencies it is missing, in one plan that you confirm once.

## Install

Install it from the Customize app (Marketplace), or from the command line:

```bash
obop modules install-from purchase
```

Needs a database and administrator rights.

## Build your own

[The module guide shows how apps like this one are written](/docs/reference/module-manifest/)
