/admin/items
Path: /admin/items
Namespace: admin
Resource: items
Overview
The Item resource represents sellable products with variants, properties, pricing, inventory tracking, and multi-domain support. Items are used in invoice lines and can be associated with addresses.
Relationships
Model Associations
Item connects to the following resources:
Address
- Type:
belongs_toviaseller_idoraddress_id - Field:
seller_id,address_id - Description: Address that owns this item. Items can be associated with addresses for seller/buyer-specific product catalogs.
- Reverse: Address has many items (
address.items) - API Access:
- Direct: Included in item response as
seller_idoraddress_id - Filter:
GET /admin/items.json?q[address_id_eq]=123 - Nested:
GET /admin/addresses/:id/items.json(items owned by address)
- Direct: Included in item response as
- Related Documentation: Addresses
- Example: Get all items owned by an address:javascript
const items = await fetch('/admin/addresses/123/items.json', { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json());
Domain
- Type:
belongs_toviahost_id - Field:
host_id - Description: Tenant domain for multi-tenant scoping. Items belong to a domain.
- Reverse: Domain has many items (
domain.items) - API Access:
- Direct: Included in item response as
host_id - Filter:
GET /admin/items.json?q[host_id_eq]=1
- Direct: Included in item response as
- Related Documentation: Domains
Domain[] (Multi-Domain Visibility)
- Type:
has_and_belongs_to_manyviadomains_itemsjoin table - Description: Domains with access to this item. Items can be visible to multiple domains (multi-domain visibility).
- Reverse: Domain has many items via
domains_itemsjoin table - API Access:
- Direct: Included in item response as
domainsarray (if present) - Filter:
GET /admin/items.json?q[domains_id_eq]=1(items visible to domain)
- Direct: Included in item response as
- Example: Filter items visible to a specific domain:javascript
const items = await fetch('/admin/items.json?q[domains_id_eq]=1', { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json());
AccountingAccount (Multiple Accounts)
- Type:
belongs_tovia multiple foreign keys - Fields:
credit_account_id,debit_account_id,tax_credit_account_id,tax_debit_account_id - Description: Accounting accounts for credit, debit, and tax entries. Used for automatic accounting when items are used in invoice lines.
- Reverse: AccountingAccount has many items (via credit/debit/tax account assignments)
- API Access:
- Direct:
item.credit_account_id,item.debit_account_id, etc. - Filter:
GET /admin/items.json?q[credit_account_id_eq]=10
- Direct:
- Related Documentation: Accounting
Product
- Type:
belongs_toviaproduct_id(optional) - Field:
product_id - Description: Product template this item was created from. Items can be created from products in the catalog.
- Reverse: Product has many items (
product.items) - API Access:
- Direct:
item.product_id(may be null) - Filter:
GET /admin/items.json?q[product_id_eq]=123 - Nested:
GET /admin/products/:id/items.json(items created from product)
- Direct:
- Related Documentation: Catalog
Reverse Associations
The following resources connect to Item:
InvoiceLine (Multiple Roles)
- Type:
has_manyon InvoiceLine viaseller_item_idandbuyer_item_id - Description: Invoice lines using this item as seller item or buyer item. Items are used in invoice lines to represent products in transactions.
- API Access:
- Filter:
GET /admin/invoice_lines.json?q[seller_item_id_eq]=123- Lines using item as seller_itemGET /admin/invoice_lines.json?q[buyer_item_id_eq]=123- Lines using item as buyer_item
- Via Invoice:
GET /admin/invoices/:id/invoice_lines.json(then filter by item_id)
- Filter:
- Related Documentation: Invoice Lines
- Example: Get all invoice lines using this item as seller item:javascript
const invoiceLines = await fetch('/admin/invoice_lines.json?q[seller_item_id_eq]=123', { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json());
Relationship Patterns
Using Items in Invoice Lines
- Use Case: Add an item to an invoice as a line item
- Example: Create invoice with item in invoice line:javascript
const invoice = await fetch('/admin/invoices.json', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ invoice: { invoice_type: 'invoice', seller_id: 1, buyer_id: 2, date: '2024-01-15', invoice_lines_attributes: [ { seller_item_id: 123, // Item ID quantity: 10, unit_price_cents: 10000 } ] } }) }).then(r => r.json());
Filtering Items by Owner
- Use Case: Get all items owned by a specific address
- Example:javascript
const addressItems = await fetch('/admin/items.json?q[address_id_eq]=123', { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json());
Multi-Domain Item Visibility
- Use Case: Make an item visible to multiple domains
- Example: When creating an item, specify domains:javascript
const item = await fetch('/admin/items.json', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ item: { code: 'PROD-001', name: 'Widget', address_id: 1, domain_ids: [1, 2, 3] // Visible to multiple domains } }) }).then(r => r.json());
Key Features
- Product Catalog: Manage product information, descriptions, images
- Inventory Tracking:
inventory_quantity,inventory_policyfor stock management - Pricing: Multiple price fields (price, cost, vendor_price, min_price, compare_at_price)
- Tax Configuration:
tax_rate,tax_typefor tax calculation - Accounting Integration:
debit_account_id,credit_account_idfor automatic accounting - Multi-currency: Currency support with exchange rates
- Variants & Properties: Support for product variants and custom properties
- Domain Scoping: Items can be associated with multiple domains
Inventory Management
Items support inventory tracking:
inventory_quantity: Current stock levelinventory_policy: Inventory management policy- Scopes:
in_stock,out_of_stock
Available Operations
List Items (GET)
Endpoint: GET /admin/items.json
Query Parameters:
q[field_predicate]=value- Ransack query filters for advanced filteringscope=name- Apply named scope (e.g.,in_stock,out_of_stock,drafts)page=N- Page number for paginationper_page=N- Items per page (default: 25)
Request Examples:
curl -X GET "https://your-company.erpax.com/admin/items.json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie"curl -X GET "https://your-company.erpax.com/admin/items.json?q[name_cont]=Product" \
-H "Accept: application/json"curl -X GET "https://your-company.erpax.com/admin/items.json?q[price_gteq]=10000&q[price_lteq]=50000" \
-H "Accept: application/json"curl -X GET "https://your-company.erpax.com/admin/items.json?scope=in_stock&q[currency_code_eq]=USD" \
-H "Accept: application/json"JavaScript Example:
const response = await fetch('/admin/items.json', {
credentials: 'include',
headers: { 'Accept': 'application/json' }
});
const data = await response.json();Response (200 OK):
{
"items": [
{
"id": 1,
"code": "PROD-001",
"name": "Widget A",
"description": "High-quality widget",
"price_cents": 10000,
"price": "100.00",
"cost_cents": 5000,
"currency_code": "USD",
"inventory_quantity": 100,
"tax_rate": 0.1,
"tax_type": "vat",
"created_at": "2024-01-10T10:00:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 50,
"total_pages": 5,
"total_count": 234
}
}Show Item (GET /:id)
Endpoint: GET /admin/items/:id.json
Request Example:
curl -X GET "https://your-company.erpax.com/admin/items/1.json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie"JavaScript Example:
const response = await fetch('/admin/items/1.json', {
credentials: 'include',
headers: { 'Accept': 'application/json' }
});
const item = await response.json();Response (200 OK):
{
"item": {
"id": 1,
"code": "PROD-001",
"name": "Widget A",
"description": "High-quality widget",
"price_cents": 10000,
"price": "100.00",
"cost_cents": 5000,
"vendor_price_cents": 6000,
"currency_code": "USD",
"inventory_quantity": 100,
"inventory_policy": "continue",
"tax_rate": 0.1,
"tax_type": "vat",
"unit": "piece",
"address_id": 1,
"debit_account_id": 10,
"credit_account_id": 20,
"created_at": "2024-01-10T10:00:00Z",
"updated_at": "2024-01-15T12:00:00Z"
}
}Create Item (POST)
Endpoint: POST /admin/items.json
Request Example:
curl -X POST "https://your-company.erpax.com/admin/items.json" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie" \
-d '{
"item": {
"code": "PROD-002",
"name": "Widget B",
"description": "Premium widget",
"price_cents": 20000,
"cost_cents": 10000,
"currency_code": "USD",
"inventory_quantity": 50,
"tax_rate": 0.1,
"tax_type": "vat",
"unit": "piece",
"address_id": 1
}
}'JavaScript Example:
const response = await fetch('/admin/items.json', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
item: {
code: 'PROD-002',
name: 'Widget B',
description: 'Premium widget',
price_cents: 20000,
cost_cents: 10000,
currency_code: 'USD',
inventory_quantity: 50,
tax_rate: 0.1,
tax_type: 'vat',
unit: 'piece',
address_id: 1
}
})
});
const item = await response.json();Response (201 Created):
{
"item": {
"id": 2,
"code": "PROD-002",
"name": "Widget B",
"price_cents": 20000,
"inventory_quantity": 50,
"created_at": "2024-01-15T14:30:00Z"
}
}Response (422 Unprocessable Entity):
{
"errors": {
"code": ["has already been taken"],
"name": ["can't be blank"]
}
}Update Item (PATCH /:id)
Endpoint: PATCH /admin/items/:id.json
Request Example:
curl -X PATCH "https://your-company.erpax.com/admin/items/1.json" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie" \
-d '{
"item": {
"price_cents": 12000,
"inventory_quantity": 95
}
}'JavaScript Example:
const response = await fetch('/admin/items/1.json', {
method: 'PATCH',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
item: {
price_cents: 12000,
inventory_quantity: 95
}
})
});
const item = await response.json();Response (200 OK):
{
"item": {
"id": 1,
"price_cents": 12000,
"inventory_quantity": 95,
"updated_at": "2024-01-15T15:00:00Z"
}
}Delete Item (DELETE /:id)
Endpoint: DELETE /admin/items/:id.json
Request Example:
curl -X DELETE "https://your-company.erpax.com/admin/items/1.json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie"JavaScript Example:
const response = await fetch('/admin/items/1.json', {
method: 'DELETE',
credentials: 'include',
headers: { 'Accept': 'application/json' }
});Response (204 No Content):
(empty response)Response (422 Unprocessable Entity):
{
"error": "Cannot delete item with associated invoice lines"
}Batch Actions
Endpoint: POST /admin/items/batch_action.json
Available batch actions:
- update - Update selected items with batch form fields
Request Example:
curl -X POST "https://your-company.erpax.com/admin/items/batch_action.json" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Cookie: session_cookie" \
-d '{
"batch_action": "update",
"collection_selection": [1, 2, 3],
"item": {
"tax_rate": 0.2
}
}
}'Scopes
- all (default) - All items
- drafts - Draft items
- free - Free items (price is 0 or nil)
- tax_free - Items with no tax
- losing - Items where price is less than cost
- earning - Items where price is greater than cost
- in_stock - Items with inventory_quantity > 0
- out_of_stock - Items with inventory_quantity <= 0
Usage Example:
GET /admin/items.json?scope=in_stock
GET /admin/items.json?scope=out_of_stock
GET /admin/items.json?scope=draftsFilters
Available filters for searching and filtering items:
- code_or_name_or_description_cont - Text filter (autofocus) - Search by code, name, or description (contains)
- product - Select filter - Filter by product
- address - Select filter - Filter by address
- code - Text filter - Filter by item code
- barcode - Text filter - Filter by barcode
- name_cont - Text filter - Search by name (contains)
- description_cont - Text filter - Search by description (contains)
- unit - Text filter - Filter by unit
- period - Text filter - Filter by period
- currency_code - Select filter (multiple) - Filter by currency code
- price - Numeric range filter - Filter by price range
- cost - Numeric range filter - Filter by cost range
- vendor_price - Numeric range filter - Filter by vendor price range
- tax_rate - Numeric range filter - Filter by tax rate range
- tax_type - Select filter (multiple) - Filter by tax type
- inventory_quantity - Numeric range filter - Filter by inventory quantity range
- inventory_policy - Select filter - Filter by inventory policy
- visibility - Select filter (multiple) - Filter by visibility level
- domain - Select filter (multiple) - Filter by domain
- tag - Select filter (multiple) - Filter by tags
- debit_account - Filter by debit account
- credit_account - Filter by credit account
- created_at - Date filter - Filter by creation date
- updated_at - Date filter - Filter by update date
Filtering Examples:
# Search by name or code
GET /admin/items.json?q[code_or_name_cont]=Widget
# Filter by price range
GET /admin/items.json?q[price_gteq]=10000&q[price_lteq]=50000
# Filter by inventory
GET /admin/items.json?q[inventory_quantity_gt]=0
GET /admin/items.json?scope=in_stock
GET /admin/items.json?scope=out_of_stock
# Filter by currency and tax
GET /admin/items.json?q[currency_code_eq]=USD&q[tax_type_eq]=vatBusiness Rules
- Code Uniqueness: Item codes must be unique within a domain
- Inventory Tracking: Inventory quantity is automatically updated when items are used in invoice lines
- Price Validation: Price must be greater than or equal to cost (unless configured otherwise)
- Domain Scoping: Items are automatically scoped to the current domain
- Accounting Integration: Accounting accounts are automatically assigned based on item configuration
Nested Resources
Items can be nested under addresses:
- Address Items - /admin/addresses/:id/items - View items associated with a specific address