Elilta ERP

API Documentation

Docs/Overview
API v1 — Product module is the primary integration target

Elilta ERP API

A multi-tenant ERP API — one tenant per business, one business type per tenant (Product, Service, or Manufacturing). This guide covers everything shared across all three, and is your starting point for integrating any client against the API.

Multi-Tenant Sanctum Bearer Auth OTP-only, passwordless Role & Permission (RBAC)

3

Business Types

300+

Product Endpoints

150+

Common/Shared Endpoints

2

Role Tiers (Owner / Employee)

Base URLs

Local Development
http://localhost:8000/api/v1
Production
https://product.elilta.cloud/api/v1
Domain-prefixed routing: every endpoint lives under its owning domain — /product, /service, /manufacturing, /crm, /banking, /hr, /auth — there are no bare, ungrouped resource paths. The onboarding endpoints under /tenant/* are the one deliberate exception (they run before a tenant/session exists).

Multi-Tenant Setup — the X-Tenant header

Every business ("tenant") has its own isolated database. Once a user is authenticated, every subsequent request must identify which tenant it belongs to by sending an X-Tenant header set to the tenant's id (e.g. choami_product) — the same value returned as tenant on the logged-in user object. Omitting it on a protected endpoint will fail to resolve tenancy and the request will be rejected.

HTTP
GET /api/v1/product/items HTTP/1.1
Host: localhost:8000
Authorization: Bearer 92|zOdLosVaYPdaz8DQUOs4XXoavWvK1gV8bsikXbWX50c0bd41
X-Tenant: choami_product
Accept: application/json

API Standards

RESTful JSON

All requests/responses use application/json. Standard verbs: GET (read), POST (create), PUT/PATCH (update), DELETE (soft-delete).

Sanctum Bearer Tokens

Stateless personal-access tokens (Laravel Sanctum). No expiry/refresh cycle — a token is valid until POST /auth/logout revokes it.

Consistent Envelope

Every response includes success and message; list endpoints add pagination in meta.

Standard Response Envelope

JSON Success
{
  "success": true,
  "message": "Items retrieved successfully",
  "data": { /* resource / list */ },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 248,
    "last_page": 17
  }
}
JSON Error
{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "phone": ["This field is required."]
  }
}
Docs/Registration & OTP

Registration & OTP Verification

Elilta is passwordless — no password is ever collected. Every phone number is verified with an OTP before an account is created or a session is issued. These are the first endpoints any client integration touches.

New Company Sign-up Flow

1

Check availability

check-registration

2

User enters OTP

sent via SMS (mock: 1234)

3

Register

creates tenant + owner

Token issued

tenant DB seeded

POST /tenant/check-registration Public

Optional pre-submit check — reports whether the phone or company name is already taken, so the sign-up form can flag the specific conflicting field without losing the user's input.

Request Body

application/json
{
  "phone": "0911909090",
  "company_name": "choami_product"
}

Response 200

200 OK
{
  "phone_exists": false,
  "company_exists": false
}
POST /tenant/register Public · rate-limited

Creates a new tenant (company), provisions its isolated database, seeds it based on service_type, creates the first user with the Owner role, and returns a session token — all gated by a verified OTP, no password involved.

Request Body

application/json
{
  "name": "Abebe Kebede",
  "company_name": "choami_product",
  "phone": "0911909090",
  "service_type": "products",
  "otp": "1234"
}

Response 200

200 OK
{
  "user": {
    "id": 1,
    "name": "Abebe Kebede",
    "phone": "0911909090",
    "tenant": "choami_product",
    "company_code": "78268",
    "service_type": "products",
    "role": { "name": "owner", "displayName": "Super Admin" },
    "permissions": [ "item-create", /* ...all permissions for this business type */ ]
  },
  "token": "92|zOdLosVaYPdaz8DQUOs4XXoavWvK1gV8bsikXbWX50c0bd41",
  "service_type": "products"
}

Fields

FieldTypeNotes
service_typestringOne of products, service, manufacturing — permanently fixes which module set the tenant gets.
company_namestringMust be a single unbroken token (no spaces) — becomes the tenant id.
otpstringMock OTP is 1234 in this environment; real SMS delivery is not yet wired.
Random company code: a unique, random 5-digit company_code is generated at registration — unrelated to the tenant id/database name. This is what employees use, alongside their phone, to log in (see below), since their phone is never searched for across tenants.

Login

Existing users (owners and employees) authenticate with phone + OTP. An employee's phone lives only inside their own tenant's database and is never scanned for across tenants — the company code is what identifies which one to check.

POST /tenant/login-otp Public · rate-limited

Verifies the OTP and issues a Sanctum token. company_code is optional for the company owner (resolved directly via a central phone↔tenant match) but required for employees — without it, only the owner shortcut is checked; no tenant database is scanned.

Request Body

application/json
{
  "phone": "0911909090",
  "otp": "1234",
  "company_code": "78268" // optional for owner
}

Response 200

200 OK
{
  "user": { /* same shape as register */ },
  "token": "93|7wEnZ5Pk9IFXoZ5zE52fmGhs6HNFn4Da2HCpC7Qefa771a50",
  "tenant_id": "choami_product"
}

Error Responses

422 No account found — for this phone (and company code, if supplied). Offer to register instead.
422 Invalid OTPotp field did not match.
POST /tenant/check-phone Public

Optional — checks whether a phone belongs to a company owner (central, O(1) lookup only; never scans tenant databases). Body: { phone, company_code? }{ exists: boolean }.

Authenticated Requests

Every request after login must carry both headers below. Missing either one will fail to authenticate/resolve tenancy.

cURL
curl -X GET "http://localhost:8000/api/v1/auth/user" \
  -H "Authorization: Bearer {token}" \
  -H "X-Tenant: {tenant_id}" \
  -H "Accept: application/json"
POST /auth/logout Bearer Token Required

Revokes the current Sanctum token server-side. No request body.

Product Integration Flow

The Product module is the primary implementation target right now. Once you've completed Registration/Login above, this is the recommended order to build a Product-type integration — each step links straight to its full reference on the Product page.

1

Fetch session context

GET /auth/user for the logged-in user's role/permissions, GET /auth/roles + GET /auth/permissions if building role management UI.

2

Set up the catalog foundation

Warehouses (/product/warehouses) → Item Units (/product/item-units) → Item Categories (/product/item-categories). A default warehouse and base units already exist from seeding.

3

Create Items

POST /product/items with pricing, then opening stock via POST /product/items/{item}/stocks.

4

Add Parties (customers/suppliers)

POST /crm/parties — Common/shared, not under /product, since Service & Manufacturing also use it.

5

Transact: Sales & Purchases

POST /product/sales and POST /product/purchases — both support orders, proformas/GRNs, confirmations and returns.

6

Record payments & reconcile

/product/payment-ins, /product/payment-outs, plus /banking/* for bank/cash/cheque handling.

Report & monitor

GET /analytics/dashboard for the summary widgets, /product/reports/* for the full 36-endpoint reporting suite.

Common / Shared Endpoints

These endpoints are identical no matter the tenant's business type — used by Product today, and by Service/Manufacturing once those go live.

Auth, Roles & Users — prefix /auth

GET /auth/roles permission: role-read

List roles. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Roles retrieved successfully",
  "data": {
    "roles": [
      {
        "id": 5,
        "name": "cashier",
        "display_name": "Cashier",
        "users_count": 4,
        "permissions": [
          {
            "id": 10,
            "display_name": "Create Party",
            "name": "party-create"
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /auth/roles permission: role-create

Create a new role.

Request Body

application/json
{
  "display_name": "Cashier",
  "permissions": [
    10,
    11,
    22,
    23
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Role created successfully",
  "data": {
    "id": 5,
    "name": "cashier",
    "display_name": "Cashier",
    "users_count": 4,
    "permissions": [
      {
        "id": 10,
        "display_name": "Create Party",
        "name": "party-create"
      }
    ]
  }
}
GET /auth/roles/{role} permission: role-read

Get a single role by id.

Response 200

200 OK
{
  "success": true,
  "message": "Role retrieved successfully",
  "data": {
    "id": 5,
    "name": "cashier",
    "display_name": "Cashier",
    "users_count": 4,
    "permissions": [
      {
        "id": 10,
        "display_name": "Create Party",
        "name": "party-create"
      }
    ]
  }
}
PUT /auth/roles/{role} permission: role-update

Update an existing role.

Request Body

application/json
{
  "display_name": "Cashier",
  "permissions": [
    10,
    11,
    22,
    23
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Role updated successfully",
  "data": {
    "id": 5,
    "name": "cashier",
    "display_name": "Cashier",
    "users_count": 4,
    "permissions": [
      {
        "id": 10,
        "display_name": "Create Party",
        "name": "party-create"
      }
    ]
  }
}
DELETE /auth/roles/{role} permission: role-delete

Soft-delete a role (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Role deleted successfully",
  "data": null
}
GET /auth/permissions permission: role-read

List every permission available to this tenant's business type — used to populate role-creation checkboxes.

Response 200

200 OK
{
  "success": true,
  "message": "Permissions retrieved successfully",
  "data": {
    "permissions": [
      {
        "id": 10,
        "display_name": "Create Party",
        "name": "party-create"
      }
    ]
  }
}
POST /auth/invite permission: user-create

Invite a new employee by phone — creates their user account and assigns a role. No password; they sign in via OTP + company code.

Request Body

application/json
{
  "name": "Selamawit Tesfaye",
  "phone": "0911909090",
  "role": 3
}

Response 200

200 OK
{
  "success": true,
  "message": "User invited successfully",
  "data": {
    "user": {
      "id": 12,
      "name": "Selamawit Tesfaye",
      "phone": "0911909090"
    },
    "token": "3|abcdef...",
    "tenant_id": "blue-nile-trading",
    "password_generated": false
  }
}
PUT /auth/users/{user} permission: user-update

Update another user's name/phone/role (owner/admin action, not self-service profile editing).

Request Body

application/json
{
  "name": "Selamawit Tesfaye",
  "phone": "0911909090",
  "role": 3
}

Response 200

200 OK
{
  "success": true,
  "message": "User updated successfully",
  "data": {
    "id": 12,
    "name": "Selamawit Tesfaye"
  }
}
GET /auth/tenant-users permission: user-read

List every user account in this tenant, with their assigned role.

Response 200

200 OK
{
  "success": true,
  "message": "Tenant users retrieved successfully",
  "data": {
    "users": [
      {
        "id": 12,
        "name": "Selamawit Tesfaye",
        "phone": "0911909090",
        "role": {
          "id": 1,
          "name": "owner"
        }
      }
    ]
  }
}

User Profile

GET /auth/profile permission: profile-read

Get the caller's own profile.

Response 200

200 OK
{
  "success": true,
  "message": "Profile retrieved successfully",
  "data": {
    "id": 12,
    "name": "Selamawit Tesfaye",
    "phone": "0911909090",
    "email": "0911909090@elilta.cloud"
  }
}
PUT /auth/profile permission: profile-update

Update the caller's own profile (name/phone; no password field exists).

Request Body

application/json
{
  "name": "Selamawit Tesfaye",
  "phone": "0911909090"
}

Response 200

200 OK
{
  "success": true,
  "message": "Profile updated successfully",
  "data": {
    "id": 12,
    "name": "Selamawit Tesfaye"
  }
}
GET /auth/user permission: Bearer Token Required

Full session object — user + role + permissions + tenant + company_code. Called on every app load to hydrate the client session.

Response 200

200 OK
{
  "id": 12,
  "name": "Selamawit Tesfaye",
  "email": "0911909090@elilta.cloud",
  "phone": "0911909090",
  "otp_verified": true,
  "tenant": "blue-nile-trading",
  "company_code": "78268",
  "service_type": "products",
  "tenant_service_type": "products",
  "income_expense_balance": 15230.5,
  "role": {
    "id": 1,
    "name": "owner",
    "displayName": "Owner"
  },
  "permissions": [
    "party-read",
    "item-create",
    // ...full permission list for this business type
  ]
}

HR & Payroll — prefix /hr

GET /hr/employees permission: employee-read

List employees. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Employees retrieved successfully",
  "data": {
    "employees": [
      {
        "id": 7,
        "user_id": 12,
        "user": {
          "id": 12,
          "name": "Selamawit Tesfaye",
          "email": "0911909090@elilta.cloud",
          "phone": "0911909090",
          "role": {
            "id": 3,
            "name": "cashier",
            "displayName": "Cashier"
          }
        },
        "tin_no": "0012345678",
        "basic_salary": 8500,
        "formatted_basic_salary": "8,500.00",
        "job_title": "Cashier",
        "position_allowance": 500,
        "transport_allowance": 600,
        "employment_status": "active",
        "hourly_rate": 47.22,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /hr/employees permission: employee-create

Create a new employee.

Request Body

application/json
{
  "name": "Selamawit Tesfaye",
  "phone": "0911909090",
  "role_id": 3,
  "tin_no": "0012345678",
  "basic_salary": 8500,
  "job_title": "Cashier",
  "position_allowance": 500,
  "transport_allowance": 600,
  "transport_taxable": 0,
  "employment_status": "active"
}

Response 201

201 OK
{
  "success": true,
  "message": "Employee created successfully",
  "data": {
    "id": 7,
    "user_id": 12,
    "user": {
      "id": 12,
      "name": "Selamawit Tesfaye",
      "email": "0911909090@elilta.cloud",
      "phone": "0911909090",
      "role": {
        "id": 3,
        "name": "cashier",
        "displayName": "Cashier"
      }
    },
    "tin_no": "0012345678",
    "basic_salary": 8500,
    "formatted_basic_salary": "8,500.00",
    "job_title": "Cashier",
    "position_allowance": 500,
    "transport_allowance": 600,
    "employment_status": "active",
    "hourly_rate": 47.22,
    "created_at": "Jul 18, 2026"
  }
}
GET /hr/employees/{employee} permission: employee-read

Get a single employee by id.

Response 200

200 OK
{
  "success": true,
  "message": "Employee retrieved successfully",
  "data": {
    "id": 7,
    "user_id": 12,
    "user": {
      "id": 12,
      "name": "Selamawit Tesfaye",
      "email": "0911909090@elilta.cloud",
      "phone": "0911909090",
      "role": {
        "id": 3,
        "name": "cashier",
        "displayName": "Cashier"
      }
    },
    "tin_no": "0012345678",
    "basic_salary": 8500,
    "formatted_basic_salary": "8,500.00",
    "job_title": "Cashier",
    "position_allowance": 500,
    "transport_allowance": 600,
    "employment_status": "active",
    "hourly_rate": 47.22,
    "created_at": "Jul 18, 2026"
  }
}
PUT /hr/employees/{employee} permission: employee-update

Update an existing employee.

Request Body

application/json
{
  "name": "Selamawit Tesfaye",
  "phone": "0911909090",
  "role_id": 3,
  "tin_no": "0012345678",
  "basic_salary": 8500,
  "job_title": "Cashier",
  "position_allowance": 500,
  "transport_allowance": 600,
  "transport_taxable": 0,
  "employment_status": "active"
}

Response 200

200 OK
{
  "success": true,
  "message": "Employee updated successfully",
  "data": {
    "id": 7,
    "user_id": 12,
    "user": {
      "id": 12,
      "name": "Selamawit Tesfaye",
      "email": "0911909090@elilta.cloud",
      "phone": "0911909090",
      "role": {
        "id": 3,
        "name": "cashier",
        "displayName": "Cashier"
      }
    },
    "tin_no": "0012345678",
    "basic_salary": 8500,
    "formatted_basic_salary": "8,500.00",
    "job_title": "Cashier",
    "position_allowance": 500,
    "transport_allowance": 600,
    "employment_status": "active",
    "hourly_rate": 47.22,
    "created_at": "Jul 18, 2026"
  }
}
DELETE /hr/employees/{employee} permission: employee-delete

Soft-delete a employee (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Employee deleted successfully",
  "data": null
}
GET /hr/employees/active/count permission: employee-read

Count of currently-active employees.

Response 200

200 OK
{
  "success": true,
  "message": "Count retrieved successfully",
  "data": {
    "count": 15
  }
}
POST /hr/employees/{employee}/send-invite permission: employee-update

Send (or resend) the login-instructions SMS to this employee — phone, company code, and OTP instructions.

Response 200

200 OK
{
  "success": true,
  "message": "Invitation sent successfully",
  "data": null
}
GET /hr/employees/{employee}/salary-histories permission: employee-read

This employee's payroll history across all salary months.

Response 200

200 OK
{
  "success": true,
  "message": "Histories retrieved successfully",
  "data": {
    "histories": [
      {
        "id": 22,
        "salary_month_id": 4,
        "employee_id": 7,
        "is_editable": true,
        "employee": {
          "id": 7,
          "job_title": "Cashier",
          "user": {
            "id": 12,
            "name": "Selamawit Tesfaye"
          }
        },
        "basic_salary": 8500,
        "position_allowance": 500,
        "transport_allowance": 600,
        "days_of_month": 30,
        "gross_salary": 9600,
        "income_tax": 1145,
        "total_deduction": 1740,
        "net_pay": 7860,
        "formatted": {
          "net_pay": "7,860.00",
          "gross_salary": "9,600.00"
        },
        "created_at": "Jul 18, 2026"
      }
    ]
  }
}
GET /hr/salary-months permission: salary_month-read

List salary months. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Salary Months retrieved successfully",
  "data": {
    "salary_months": [
      {
        "id": 4,
        "title": "July 2026 Payroll",
        "month": 7,
        "month_name": "July",
        "year": 2026,
        "status": "open",
        "is_editable": true,
        "total_net_pay": "182,340.00",
        "bank_account": {
          "id": 2,
          "name": "Commercial Bank of Ethiopia"
        },
        "created_by_user": {
          "id": 1,
          "name": "Selamawit Tesfaye"
        },
        "salary_histories_count": 12,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /hr/salary-months permission: salary_month-create

Create a new salary month.

Request Body

application/json
{
  "title": "July 2026 Payroll",
  "month": 7,
  "year": 2026
}

Response 201

201 OK
{
  "success": true,
  "message": "Salary Month created successfully",
  "data": {
    "id": 4,
    "title": "July 2026 Payroll",
    "month": 7,
    "month_name": "July",
    "year": 2026,
    "status": "open",
    "is_editable": true,
    "total_net_pay": "182,340.00",
    "bank_account": {
      "id": 2,
      "name": "Commercial Bank of Ethiopia"
    },
    "created_by_user": {
      "id": 1,
      "name": "Selamawit Tesfaye"
    },
    "salary_histories_count": 12,
    "created_at": "Jul 18, 2026"
  }
}
GET /hr/salary-months/{salary_month} permission: salary_month-read

Get a single salary month by id.

Response 200

200 OK
{
  "success": true,
  "message": "Salary Month retrieved successfully",
  "data": {
    "id": 4,
    "title": "July 2026 Payroll",
    "month": 7,
    "month_name": "July",
    "year": 2026,
    "status": "open",
    "is_editable": true,
    "total_net_pay": "182,340.00",
    "bank_account": {
      "id": 2,
      "name": "Commercial Bank of Ethiopia"
    },
    "created_by_user": {
      "id": 1,
      "name": "Selamawit Tesfaye"
    },
    "salary_histories_count": 12,
    "created_at": "Jul 18, 2026"
  }
}
DELETE /hr/salary-months/{salary_month} permission: salary_month-delete

Soft-delete a salary month (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Salary Month deleted successfully",
  "data": null
}
GET /hr/salary-months/{salaryMonthId}/histories permission: salary_month-read

All payroll entries within this salary month.

Response 200

200 OK
{
  "success": true,
  "message": "Histories retrieved successfully",
  "data": {
    "histories": [
      {
        "id": 22,
        "salary_month_id": 4,
        "employee_id": 7,
        "is_editable": true,
        "employee": {
          "id": 7,
          "job_title": "Cashier",
          "user": {
            "id": 12,
            "name": "Selamawit Tesfaye"
          }
        },
        "basic_salary": 8500,
        "position_allowance": 500,
        "transport_allowance": 600,
        "days_of_month": 30,
        "gross_salary": 9600,
        "income_tax": 1145,
        "total_deduction": 1740,
        "net_pay": 7860,
        "formatted": {
          "net_pay": "7,860.00",
          "gross_salary": "9,600.00"
        },
        "created_at": "Jul 18, 2026"
      }
    ]
  }
}
POST /hr/salary-months/{salaryMonth}/pay permission: salary_month-read

Mark this salary month as paid — disburses net pay to every employee via the linked bank account.

Request Body

application/json
{
  "bank_account_id": 2
}

Response 200

200 OK
{
  "success": true,
  "message": "Salary month paid successfully",
  "data": null
}
POST /hr/salary-months/{salaryMonth}/save-all permission: salary_month-update

Bulk-save every payroll entry for this salary month in a single call.

Request Body

application/json
{
  "entries": [
    {
      "employee_id": 7,
      "day_hour": 8,
      "overtime": 0
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Payroll saved successfully",
  "data": null
}
GET /hr/salary-months/{salaryMonth}/summary permission: salary_month-read

Aggregate payroll totals for this salary month (gross, deductions, net).

Response 200

200 OK
{
  "success": true,
  "message": "Summary retrieved successfully",
  "data": {
    "total_gross": 182340,
    "total_deductions": 34120,
    "total_net_pay": 148220
  }
}
POST /hr/salary-histories permission: salary_history-create

Create a new salary history.

Request Body

application/json
{
  "salary_month_id": 4,
  "employee_id": 7,
  "days_of_month": 30,
  "night_hour": 0,
  "day_hour": 8,
  "weekend_hour": 0,
  "holiday_hour": 0
}

Response 201

201 OK
{
  "success": true,
  "message": "Salary History created successfully",
  "data": {
    "id": 22,
    "salary_month_id": 4,
    "employee_id": 7,
    "is_editable": true,
    "employee": {
      "id": 7,
      "job_title": "Cashier",
      "user": {
        "id": 12,
        "name": "Selamawit Tesfaye"
      }
    },
    "basic_salary": 8500,
    "position_allowance": 500,
    "transport_allowance": 600,
    "days_of_month": 30,
    "gross_salary": 9600,
    "income_tax": 1145,
    "total_deduction": 1740,
    "net_pay": 7860,
    "formatted": {
      "net_pay": "7,860.00",
      "gross_salary": "9,600.00"
    },
    "created_at": "Jul 18, 2026"
  }
}
POST /hr/salary-histories/{salaryHistory}/recalculate permission: salary_history-create

Create a new salary history.

Request Body

application/json
{
  "salary_month_id": 4,
  "employee_id": 7,
  "days_of_month": 30,
  "night_hour": 0,
  "day_hour": 8,
  "weekend_hour": 0,
  "holiday_hour": 0
}

Response 201

201 OK
{
  "success": true,
  "message": "Salary History created successfully",
  "data": {
    "id": 22,
    "salary_month_id": 4,
    "employee_id": 7,
    "is_editable": true,
    "employee": {
      "id": 7,
      "job_title": "Cashier",
      "user": {
        "id": 12,
        "name": "Selamawit Tesfaye"
      }
    },
    "basic_salary": 8500,
    "position_allowance": 500,
    "transport_allowance": 600,
    "days_of_month": 30,
    "gross_salary": 9600,
    "income_tax": 1145,
    "total_deduction": 1740,
    "net_pay": 7860,
    "formatted": {
      "net_pay": "7,860.00",
      "gross_salary": "9,600.00"
    },
    "created_at": "Jul 18, 2026"
  }
}
GET /hr/salary-histories/{salary_history} permission: salary_history-read

Get a single salary history by id.

Response 200

200 OK
{
  "success": true,
  "message": "Salary History retrieved successfully",
  "data": {
    "id": 22,
    "salary_month_id": 4,
    "employee_id": 7,
    "is_editable": true,
    "employee": {
      "id": 7,
      "job_title": "Cashier",
      "user": {
        "id": 12,
        "name": "Selamawit Tesfaye"
      }
    },
    "basic_salary": 8500,
    "position_allowance": 500,
    "transport_allowance": 600,
    "days_of_month": 30,
    "gross_salary": 9600,
    "income_tax": 1145,
    "total_deduction": 1740,
    "net_pay": 7860,
    "formatted": {
      "net_pay": "7,860.00",
      "gross_salary": "9,600.00"
    },
    "created_at": "Jul 18, 2026"
  }
}
PUT /hr/salary-histories/{salary_history} permission: salary_history-update

Update an existing salary history.

Request Body

application/json
{
  "salary_month_id": 4,
  "employee_id": 7,
  "days_of_month": 30,
  "night_hour": 0,
  "day_hour": 8,
  "weekend_hour": 0,
  "holiday_hour": 0
}

Response 200

200 OK
{
  "success": true,
  "message": "Salary History updated successfully",
  "data": {
    "id": 22,
    "salary_month_id": 4,
    "employee_id": 7,
    "is_editable": true,
    "employee": {
      "id": 7,
      "job_title": "Cashier",
      "user": {
        "id": 12,
        "name": "Selamawit Tesfaye"
      }
    },
    "basic_salary": 8500,
    "position_allowance": 500,
    "transport_allowance": 600,
    "days_of_month": 30,
    "gross_salary": 9600,
    "income_tax": 1145,
    "total_deduction": 1740,
    "net_pay": 7860,
    "formatted": {
      "net_pay": "7,860.00",
      "gross_salary": "9,600.00"
    },
    "created_at": "Jul 18, 2026"
  }
}

CRM / Parties — prefix /crm

A "party" is a customer or supplier (or both). Shared across every business type.

GET /crm/parties permission: party-read

List partys. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Partys retrieved successfully",
  "data": {
    "parties": [
      {
        "id": 15,
        "uuid": "9f2c1a3e-...",
        "party_type": "customer",
        "legal_name": "Blue Nile Trading PLC",
        "display_name": "Blue Nile Trading",
        "email": "info@bluenile.com",
        "phone": "0911909090",
        "tin_number": "0012345678",
        "vat_no": "VAT-00981",
        "tax_reg_type": "vat_registered",
        "currency": "Birr",
        "country_default": "Ethiopia",
        "balance": 12500.75,
        "opening_balance_amount": 5000,
        "opening_balance_type": "debit",
        "opening_date": "Jan 1, 2026",
        "is_active": true,
        "billing_address": "Bole, Addis Ababa",
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /crm/parties permission: party-create

Create a new party.

Request Body

application/json
{
  "party_type": "customer",
  "legal_name": "Blue Nile Trading PLC",
  "display_name": "Blue Nile Trading",
  "email": "info@bluenile.com",
  "phone": "0911909090",
  "tin_number": "0012345678",
  "vat_no": "VAT-00981",
  "currency": "Birr",
  "country_default": "Ethiopia",
  "opening_balance_amount": 5000,
  "opening_balance_type": "debit",
  "opening_date": "2026-01-01",
  "settlement_direction": "receive",
  "billing_address": "Bole, Addis Ababa",
  "is_active": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Party created successfully",
  "data": {
    "id": 15,
    "uuid": "9f2c1a3e-...",
    "party_type": "customer",
    "legal_name": "Blue Nile Trading PLC",
    "display_name": "Blue Nile Trading",
    "email": "info@bluenile.com",
    "phone": "0911909090",
    "tin_number": "0012345678",
    "vat_no": "VAT-00981",
    "tax_reg_type": "vat_registered",
    "currency": "Birr",
    "country_default": "Ethiopia",
    "balance": 12500.75,
    "opening_balance_amount": 5000,
    "opening_balance_type": "debit",
    "opening_date": "Jan 1, 2026",
    "is_active": true,
    "billing_address": "Bole, Addis Ababa",
    "created_at": "Jul 18, 2026"
  }
}
GET /crm/parties/{party} permission: party-read

Get a single party by id.

Response 200

200 OK
{
  "success": true,
  "message": "Party retrieved successfully",
  "data": {
    "id": 15,
    "uuid": "9f2c1a3e-...",
    "party_type": "customer",
    "legal_name": "Blue Nile Trading PLC",
    "display_name": "Blue Nile Trading",
    "email": "info@bluenile.com",
    "phone": "0911909090",
    "tin_number": "0012345678",
    "vat_no": "VAT-00981",
    "tax_reg_type": "vat_registered",
    "currency": "Birr",
    "country_default": "Ethiopia",
    "balance": 12500.75,
    "opening_balance_amount": 5000,
    "opening_balance_type": "debit",
    "opening_date": "Jan 1, 2026",
    "is_active": true,
    "billing_address": "Bole, Addis Ababa",
    "created_at": "Jul 18, 2026"
  }
}
PUT /crm/parties/{party} permission: party-update

Update an existing party.

Request Body

application/json
{
  "party_type": "customer",
  "legal_name": "Blue Nile Trading PLC",
  "display_name": "Blue Nile Trading",
  "email": "info@bluenile.com",
  "phone": "0911909090",
  "tin_number": "0012345678",
  "vat_no": "VAT-00981",
  "currency": "Birr",
  "country_default": "Ethiopia",
  "opening_balance_amount": 5000,
  "opening_balance_type": "debit",
  "opening_date": "2026-01-01",
  "settlement_direction": "receive",
  "billing_address": "Bole, Addis Ababa",
  "is_active": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Party updated successfully",
  "data": {
    "id": 15,
    "uuid": "9f2c1a3e-...",
    "party_type": "customer",
    "legal_name": "Blue Nile Trading PLC",
    "display_name": "Blue Nile Trading",
    "email": "info@bluenile.com",
    "phone": "0911909090",
    "tin_number": "0012345678",
    "vat_no": "VAT-00981",
    "tax_reg_type": "vat_registered",
    "currency": "Birr",
    "country_default": "Ethiopia",
    "balance": 12500.75,
    "opening_balance_amount": 5000,
    "opening_balance_type": "debit",
    "opening_date": "Jan 1, 2026",
    "is_active": true,
    "billing_address": "Bole, Addis Ababa",
    "created_at": "Jul 18, 2026"
  }
}
DELETE /crm/parties/{party} permission: party-delete

Soft-delete a party (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Party deleted successfully",
  "data": null
}
GET /crm/parties/autocomplete permission: party-read

Lightweight id+name list for autocomplete/typeahead party pickers.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "parties": [
      {
        "id": 15,
        "display_name": "Blue Nile Trading"
      }
    ]
  }
}
GET /crm/parties/filter permission: party-read

Advanced filtered party list (type, balance range, active status).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "parties": [
      {
        "id": 15,
        "uuid": "9f2c1a3e-...",
        "party_type": "customer",
        "legal_name": "Blue Nile Trading PLC",
        "display_name": "Blue Nile Trading",
        "email": "info@bluenile.com",
        "phone": "0911909090",
        "tin_number": "0012345678",
        "vat_no": "VAT-00981",
        "tax_reg_type": "vat_registered",
        "currency": "Birr",
        "country_default": "Ethiopia",
        "balance": 12500.75,
        "opening_balance_amount": 5000,
        "opening_balance_type": "debit",
        "opening_date": "Jan 1, 2026",
        "is_active": true,
        "billing_address": "Bole, Addis Ababa",
        "created_at": "Jul 18, 2026"
      }
    ]
  }
}
GET /crm/parties/search permission: party-read

Full-text party search by name, phone, or TIN.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "parties": [
      {
        "id": 15,
        "uuid": "9f2c1a3e-...",
        "party_type": "customer",
        "legal_name": "Blue Nile Trading PLC",
        "display_name": "Blue Nile Trading",
        "email": "info@bluenile.com",
        "phone": "0911909090",
        "tin_number": "0012345678",
        "vat_no": "VAT-00981",
        "tax_reg_type": "vat_registered",
        "currency": "Birr",
        "country_default": "Ethiopia",
        "balance": 12500.75,
        "opening_balance_amount": 5000,
        "opening_balance_type": "debit",
        "opening_date": "Jan 1, 2026",
        "is_active": true,
        "billing_address": "Bole, Addis Ababa",
        "created_at": "Jul 18, 2026"
      }
    ]
  }
}
GET /crm/parties/{party}/history permission: party-read

Audit-history entries for this party.

Response 200

200 OK
{
  "success": true,
  "message": "History retrieved successfully",
  "data": {
    "history": []
  }
}
GET /crm/parties/{party}/transactions permission: party-read

Every ledger transaction (sales, purchases, payments) recorded against this party.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "transactions": [
      {
        "id": 512,
        "transaction_no": "TXN-2026-000452",
        "transaction_date": "2026-07-18 10:30:00",
        "amount": 9025,
        "type": "sale",
        "direction": "in",
        "description": "Payment received for INV-2026-0042",
        "reference_number": "RCPT-2026-0033",
        "status": "completed",
        "party_balance_before": 21525.75,
        "party_balance_after": 12500.75,
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "display_name": "Blue Nile Trading"
        }
      }
    ]
  }
}
GET /crm/parties/{party}/contacts permission: party_contact-read

List party contacts. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Party Contacts retrieved successfully",
  "data": {
    "contacts": [
      {
        "id": 3,
        "party_id": 15,
        "name": "Abebe Kebede",
        "designation": "Purchasing Manager",
        "email": "abebe@bluenile.com",
        "phone": "0911909090",
        "is_primary": true,
        "preferred_contact": "phone",
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /crm/parties/{party}/contacts permission: party_contact-create

Create a new party contact.

Request Body

application/json
{
  "name": "Abebe Kebede",
  "designation": "Purchasing Manager",
  "email": "abebe@bluenile.com",
  "phone": "0911909090",
  "is_primary": true,
  "preferred_contact": "phone"
}

Response 201

201 OK
{
  "success": true,
  "message": "Party Contact created successfully",
  "data": {
    "id": 3,
    "party_id": 15,
    "name": "Abebe Kebede",
    "designation": "Purchasing Manager",
    "email": "abebe@bluenile.com",
    "phone": "0911909090",
    "is_primary": true,
    "preferred_contact": "phone",
    "created_at": "Jul 18, 2026"
  }
}
PUT /crm/contacts/{contact} permission: party_contact-update

Update an existing party contact.

Request Body

application/json
{
  "name": "Abebe Kebede",
  "designation": "Purchasing Manager",
  "email": "abebe@bluenile.com",
  "phone": "0911909090",
  "is_primary": true,
  "preferred_contact": "phone"
}

Response 200

200 OK
{
  "success": true,
  "message": "Party Contact updated successfully",
  "data": {
    "id": 3,
    "party_id": 15,
    "name": "Abebe Kebede",
    "designation": "Purchasing Manager",
    "email": "abebe@bluenile.com",
    "phone": "0911909090",
    "is_primary": true,
    "preferred_contact": "phone",
    "created_at": "Jul 18, 2026"
  }
}
DELETE /crm/contacts/{contact} permission: party_contact-delete

Soft-delete a party contact (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Party Contact deleted successfully",
  "data": null
}
GET /crm/parties/{party}/credit-settings permission: party_financial-read

List party credit settingss. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Party Credit Settingss retrieved successfully",
  "data": {
    "credit_settings": [
      {
        "id": 6,
        "party_id": 15,
        "credit_limit_enabled": true,
        "credit_limit_amount": 50000,
        "no_limit": false,
        "settlement_direction": "receive",
        "current_balance_amount": 12500.75,
        "balance_currency": "Birr",
        "allow_overdue_transactions": false,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /crm/parties/{party}/credit-settings permission: party_financial-create

Create a new party credit settings.

Request Body

application/json
{
  "credit_limit_enabled": true,
  "credit_limit_amount": 50000,
  "no_limit": false,
  "settlement_direction": "receive",
  "allow_overdue_transactions": false
}

Response 201

201 OK
{
  "success": true,
  "message": "Party Credit Settings created successfully",
  "data": {
    "id": 6,
    "party_id": 15,
    "credit_limit_enabled": true,
    "credit_limit_amount": 50000,
    "no_limit": false,
    "settlement_direction": "receive",
    "current_balance_amount": 12500.75,
    "balance_currency": "Birr",
    "allow_overdue_transactions": false,
    "created_at": "Jul 18, 2026"
  }
}
PUT /crm/parties/{party}/credit-settings permission: party_financial-*

Per-party credit limit & settlement terms.

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}
DELETE /crm/parties/{party}/credit-settings permission: party_financial-*

Per-party credit limit & settlement terms.

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}
GET /crm/parties/{party}/settings permission: party_setting-read

List party settings. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Party Settings retrieved successfully",
  "data": {
    "settings": [
      {
        "id": 9,
        "party_id": 15,
        "enable_billing_address": true,
        "enable_payment_reminder": true,
        "payment_reminder_days": 7,
        "additional_field_1_label": "License No.",
        "additional_field_1_print": true,
        "enable_loyalty_point": false,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /crm/parties/{party}/settings permission: party_setting-create

Create a new party setting.

Request Body

application/json
{
  "enable_billing_address": true,
  "enable_payment_reminder": true,
  "payment_reminder_days": 7,
  "additional_field_1_label": "License No.",
  "additional_field_1_print": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Party Setting created successfully",
  "data": {
    "id": 9,
    "party_id": 15,
    "enable_billing_address": true,
    "enable_payment_reminder": true,
    "payment_reminder_days": 7,
    "additional_field_1_label": "License No.",
    "additional_field_1_print": true,
    "enable_loyalty_point": false,
    "created_at": "Jul 18, 2026"
  }
}
PUT /crm/parties/{party}/settings permission: party_setting-*

Per-item settings (tax applicability, stock-maintenance flags, etc.).

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}
DELETE /crm/parties/{party}/settings permission: party_setting-*

Per-item settings (tax applicability, stock-maintenance flags, etc.).

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}

Cash, Bank & Cheques — prefix /banking

GET /banking/bank-accounts permission: bank_account-read

List bank accounts. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Accounts retrieved successfully",
  "data": {
    "bank_accounts": [
      {
        "id": 2,
        "uuid": "a1b2c3d4-...",
        "bank_name": "Commercial Bank of Ethiopia",
        "opening_balance": 100000,
        "opening_balance_date": "Jan 1, 2026",
        "account_holder_name": "Elilta Trading PLC",
        "account_number": "1000234567890",
        "branch_name": "Bole Branch",
        "current_balance": 148230.5,
        "is_active": true,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /banking/bank-accounts permission: bank_account-create

Create a new bank account.

Request Body

application/json
{
  "bank_name": "Commercial Bank of Ethiopia",
  "opening_balance": 100000,
  "opening_balance_date": "2026-01-01",
  "print_detail_on_invoice": true,
  "account_holder_name": "Elilta Trading PLC",
  "account_number": "1000234567890",
  "branch_name": "Bole Branch"
}

Response 201

201 OK
{
  "success": true,
  "message": "Bank Account created successfully",
  "data": {
    "id": 2,
    "uuid": "a1b2c3d4-...",
    "bank_name": "Commercial Bank of Ethiopia",
    "opening_balance": 100000,
    "opening_balance_date": "Jan 1, 2026",
    "account_holder_name": "Elilta Trading PLC",
    "account_number": "1000234567890",
    "branch_name": "Bole Branch",
    "current_balance": 148230.5,
    "is_active": true,
    "created_at": "Jul 18, 2026"
  }
}
GET /banking/bank-accounts/{bank_account} permission: bank_account-read

Get a single bank account by id.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Account retrieved successfully",
  "data": {
    "id": 2,
    "uuid": "a1b2c3d4-...",
    "bank_name": "Commercial Bank of Ethiopia",
    "opening_balance": 100000,
    "opening_balance_date": "Jan 1, 2026",
    "account_holder_name": "Elilta Trading PLC",
    "account_number": "1000234567890",
    "branch_name": "Bole Branch",
    "current_balance": 148230.5,
    "is_active": true,
    "created_at": "Jul 18, 2026"
  }
}
PUT /banking/bank-accounts/{bank_account} permission: bank_account-update

Update an existing bank account.

Request Body

application/json
{
  "bank_name": "Commercial Bank of Ethiopia",
  "opening_balance": 100000,
  "opening_balance_date": "2026-01-01",
  "print_detail_on_invoice": true,
  "account_holder_name": "Elilta Trading PLC",
  "account_number": "1000234567890",
  "branch_name": "Bole Branch"
}

Response 200

200 OK
{
  "success": true,
  "message": "Bank Account updated successfully",
  "data": {
    "id": 2,
    "uuid": "a1b2c3d4-...",
    "bank_name": "Commercial Bank of Ethiopia",
    "opening_balance": 100000,
    "opening_balance_date": "Jan 1, 2026",
    "account_holder_name": "Elilta Trading PLC",
    "account_number": "1000234567890",
    "branch_name": "Bole Branch",
    "current_balance": 148230.5,
    "is_active": true,
    "created_at": "Jul 18, 2026"
  }
}
DELETE /banking/bank-accounts/{bank_account} permission: bank_account-delete

Soft-delete a bank account (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Bank Account deleted successfully",
  "data": null
}
POST /banking/bank-accounts/{bank_account}/update-balance permission: bank_account-update

Reconcile this bank account's balance against actual bank records.

Request Body

application/json
{
  "current_balance": 148230.5,
  "notes": "Reconciled against July bank statement"
}

Response 200

200 OK
{
  "success": true,
  "message": "Balance updated successfully",
  "data": {
    "id": 2,
    "uuid": "a1b2c3d4-...",
    "bank_name": "Commercial Bank of Ethiopia",
    "opening_balance": 100000,
    "opening_balance_date": "Jan 1, 2026",
    "account_holder_name": "Elilta Trading PLC",
    "account_number": "1000234567890",
    "branch_name": "Bole Branch",
    "current_balance": 148230.5,
    "is_active": true,
    "created_at": "Jul 18, 2026"
  }
}
GET /banking/bank-adjustments permission: bank_adjustment-read

List bank adjustments. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Adjustments retrieved successfully",
  "data": {
    "bank_adjustments": [
      {
        "id": 44,
        "transfer_date": "2026-07-18",
        "type": "debit",
        "bank_account_id": 2,
        "amount": 5000,
        "description": "Supplier payment adjustment",
        "bank_account": {
          "bank_account_id": 2,
          "bank_name": "Commercial Bank of Ethiopia"
        },
        "adjusted_by_user": {
          "user_id": 12,
          "name": "Selamawit Tesfaye"
        },
        "created_at": "2026-07-18T09:00:00.000000Z"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /banking/bank-adjustments permission: bank_adjustment-create

Create a new bank adjustment.

Request Body

application/json
{
  "transfer_date": "2026-07-18",
  "type": "debit",
  "bank_account_id": 2,
  "amount": 5000,
  "description": "Supplier payment adjustment"
}

Response 201

201 OK
{
  "success": true,
  "message": "Bank Adjustment created successfully",
  "data": {
    "id": 44,
    "transfer_date": "2026-07-18",
    "type": "debit",
    "bank_account_id": 2,
    "amount": 5000,
    "description": "Supplier payment adjustment",
    "bank_account": {
      "bank_account_id": 2,
      "bank_name": "Commercial Bank of Ethiopia"
    },
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
GET /banking/bank-adjustments/{bank_adjustment} permission: bank_adjustment-read

Get a single bank adjustment by id.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Adjustment retrieved successfully",
  "data": {
    "id": 44,
    "transfer_date": "2026-07-18",
    "type": "debit",
    "bank_account_id": 2,
    "amount": 5000,
    "description": "Supplier payment adjustment",
    "bank_account": {
      "bank_account_id": 2,
      "bank_name": "Commercial Bank of Ethiopia"
    },
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
PUT /banking/bank-adjustments/{bank_adjustment} permission: bank_adjustment-update

Update an existing bank adjustment.

Request Body

application/json
{
  "transfer_date": "2026-07-18",
  "type": "debit",
  "bank_account_id": 2,
  "amount": 5000,
  "description": "Supplier payment adjustment"
}

Response 200

200 OK
{
  "success": true,
  "message": "Bank Adjustment updated successfully",
  "data": {
    "id": 44,
    "transfer_date": "2026-07-18",
    "type": "debit",
    "bank_account_id": 2,
    "amount": 5000,
    "description": "Supplier payment adjustment",
    "bank_account": {
      "bank_account_id": 2,
      "bank_name": "Commercial Bank of Ethiopia"
    },
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
DELETE /banking/bank-adjustments/{bank_adjustment} permission: bank_adjustment-delete

Soft-delete a bank adjustment (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Bank Adjustment deleted successfully",
  "data": null
}
GET /banking/bank-transfers permission: bank_transfer-read

List bank transfers. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Transfers retrieved successfully",
  "data": {
    "bank_transfers": [
      {
        "id": 8,
        "transfer_date": "2026-07-18",
        "amount": 20000,
        "source_type": "bank",
        "source_id": 2,
        "destination_type": "cash",
        "destination_id": 5,
        "description": "Cash withdrawal for petty cash",
        "reference_number": "TRF-0008",
        "source": {
          "id": 2,
          "type": "bank",
          "name": "Commercial Bank of Ethiopia",
          "current_balance": 128230.5
        },
        "destination": {
          "id": 5,
          "type": "cash",
          "name": "Cash Account",
          "current_balance": 20000
        },
        "created_at": "2026-07-18T09:00:00.000000Z"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /banking/bank-transfers permission: bank_transfer-create

Create a new bank transfer.

Request Body

application/json
{
  "transfer_date": "2026-07-18",
  "amount": 20000,
  "source_type": "bank",
  "destination_type": "cash",
  "source_id": 2,
  "description": "Cash withdrawal for petty cash",
  "reference_number": "TRF-0008"
}

Response 201

201 OK
{
  "success": true,
  "message": "Bank Transfer created successfully",
  "data": {
    "id": 8,
    "transfer_date": "2026-07-18",
    "amount": 20000,
    "source_type": "bank",
    "source_id": 2,
    "destination_type": "cash",
    "destination_id": 5,
    "description": "Cash withdrawal for petty cash",
    "reference_number": "TRF-0008",
    "source": {
      "id": 2,
      "type": "bank",
      "name": "Commercial Bank of Ethiopia",
      "current_balance": 128230.5
    },
    "destination": {
      "id": 5,
      "type": "cash",
      "name": "Cash Account",
      "current_balance": 20000
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
GET /banking/bank-transfers/{bank_transfer} permission: bank_transfer-read

Get a single bank transfer by id.

Response 200

200 OK
{
  "success": true,
  "message": "Bank Transfer retrieved successfully",
  "data": {
    "id": 8,
    "transfer_date": "2026-07-18",
    "amount": 20000,
    "source_type": "bank",
    "source_id": 2,
    "destination_type": "cash",
    "destination_id": 5,
    "description": "Cash withdrawal for petty cash",
    "reference_number": "TRF-0008",
    "source": {
      "id": 2,
      "type": "bank",
      "name": "Commercial Bank of Ethiopia",
      "current_balance": 128230.5
    },
    "destination": {
      "id": 5,
      "type": "cash",
      "name": "Cash Account",
      "current_balance": 20000
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
PUT /banking/bank-transfers/{bank_transfer} permission: bank_transfer-update

Update an existing bank transfer.

Request Body

application/json
{
  "transfer_date": "2026-07-18",
  "amount": 20000,
  "source_type": "bank",
  "destination_type": "cash",
  "source_id": 2,
  "description": "Cash withdrawal for petty cash",
  "reference_number": "TRF-0008"
}

Response 200

200 OK
{
  "success": true,
  "message": "Bank Transfer updated successfully",
  "data": {
    "id": 8,
    "transfer_date": "2026-07-18",
    "amount": 20000,
    "source_type": "bank",
    "source_id": 2,
    "destination_type": "cash",
    "destination_id": 5,
    "description": "Cash withdrawal for petty cash",
    "reference_number": "TRF-0008",
    "source": {
      "id": 2,
      "type": "bank",
      "name": "Commercial Bank of Ethiopia",
      "current_balance": 128230.5
    },
    "destination": {
      "id": 5,
      "type": "cash",
      "name": "Cash Account",
      "current_balance": 20000
    },
    "created_at": "2026-07-18T09:00:00.000000Z"
  }
}
DELETE /banking/bank-transfers/{bank_transfer} permission: bank_transfer-delete

Soft-delete a bank transfer (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Bank Transfer deleted successfully",
  "data": null
}
GET /banking/cash-adjustments permission: cash_adjustment-read

List cash adjustments. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Cash Adjustments retrieved successfully",
  "data": {
    "cash_adjustments": [
      {
        "id": 5,
        "type": "add",
        "adjustment_date": "2026-07-18",
        "amount": 20000,
        "description": "Petty cash top-up",
        "adjusted_by_user": {
          "user_id": 12,
          "name": "Selamawit Tesfaye"
        },
        "bank_transfer_id": 8,
        "created_at": "Jul 18, 2026"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /banking/cash-adjustments permission: cash_adjustment-create

Create a new cash adjustment.

Request Body

application/json
{
  "type": "add",
  "adjustment_date": "2026-07-18",
  "amount": 20000,
  "description": "Petty cash top-up",
  "bank_transfer_id": 8
}

Response 201

201 OK
{
  "success": true,
  "message": "Cash Adjustment created successfully",
  "data": {
    "id": 5,
    "type": "add",
    "adjustment_date": "2026-07-18",
    "amount": 20000,
    "description": "Petty cash top-up",
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "bank_transfer_id": 8,
    "created_at": "Jul 18, 2026"
  }
}
GET /banking/cash-adjustments/{cash_adjustment} permission: cash_adjustment-read

Get a single cash adjustment by id.

Response 200

200 OK
{
  "success": true,
  "message": "Cash Adjustment retrieved successfully",
  "data": {
    "id": 5,
    "type": "add",
    "adjustment_date": "2026-07-18",
    "amount": 20000,
    "description": "Petty cash top-up",
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "bank_transfer_id": 8,
    "created_at": "Jul 18, 2026"
  }
}
PUT /banking/cash-adjustments/{cash_adjustment} permission: cash_adjustment-update

Update an existing cash adjustment.

Request Body

application/json
{
  "type": "add",
  "adjustment_date": "2026-07-18",
  "amount": 20000,
  "description": "Petty cash top-up",
  "bank_transfer_id": 8
}

Response 200

200 OK
{
  "success": true,
  "message": "Cash Adjustment updated successfully",
  "data": {
    "id": 5,
    "type": "add",
    "adjustment_date": "2026-07-18",
    "amount": 20000,
    "description": "Petty cash top-up",
    "adjusted_by_user": {
      "user_id": 12,
      "name": "Selamawit Tesfaye"
    },
    "bank_transfer_id": 8,
    "created_at": "Jul 18, 2026"
  }
}
DELETE /banking/cash-adjustments/{cash_adjustment} permission: cash_adjustment-delete

Soft-delete a cash adjustment (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Cash Adjustment deleted successfully",
  "data": null
}
GET /banking/cheques permission: cheque-read

List cheques. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Cheques retrieved successfully",
  "data": {
    "cheques": [
      {
        "id": 19,
        "ref_number": "CHQ-000123",
        "amount": 30000,
        "type": "receivable",
        "status": "open",
        "cheque_date": "Jul 25, 2026",
        "description": "Cheque from Blue Nile Trading",
        "updated_at": "Jul 18, 2026",
        "chequable": {
          "type": "SalesInvoice",
          "id": 44
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /banking/cheques permission: cheque-create

Create a new cheque.

Request Body

application/json
{
  "ref_number": "CHQ-000123",
  "amount": 30000,
  "cheque_date": "2026-07-25",
  "party_id": 15,
  "chequable_type": "App\Modules\Product\Models\SalesInvoice",
  "chequable_id": 44,
  "type": "receivable",
  "description": "Cheque from Blue Nile Trading"
}

Response 201

201 OK
{
  "success": true,
  "message": "Cheque created successfully",
  "data": {
    "id": 19,
    "ref_number": "CHQ-000123",
    "amount": 30000,
    "type": "receivable",
    "status": "open",
    "cheque_date": "Jul 25, 2026",
    "description": "Cheque from Blue Nile Trading",
    "updated_at": "Jul 18, 2026",
    "chequable": {
      "type": "SalesInvoice",
      "id": 44
    }
  }
}
GET /banking/cheques/{cheque} permission: cheque-read

Get a single cheque by id.

Response 200

200 OK
{
  "success": true,
  "message": "Cheque retrieved successfully",
  "data": {
    "id": 19,
    "ref_number": "CHQ-000123",
    "amount": 30000,
    "type": "receivable",
    "status": "open",
    "cheque_date": "Jul 25, 2026",
    "description": "Cheque from Blue Nile Trading",
    "updated_at": "Jul 18, 2026",
    "chequable": {
      "type": "SalesInvoice",
      "id": 44
    }
  }
}
PUT /banking/cheques/{cheque} permission: cheque-update

Update an existing cheque.

Request Body

application/json
{
  "ref_number": "CHQ-000123",
  "amount": 30000,
  "cheque_date": "2026-07-25",
  "party_id": 15,
  "chequable_type": "App\Modules\Product\Models\SalesInvoice",
  "chequable_id": 44,
  "type": "receivable",
  "description": "Cheque from Blue Nile Trading"
}

Response 200

200 OK
{
  "success": true,
  "message": "Cheque updated successfully",
  "data": {
    "id": 19,
    "ref_number": "CHQ-000123",
    "amount": 30000,
    "type": "receivable",
    "status": "open",
    "cheque_date": "Jul 25, 2026",
    "description": "Cheque from Blue Nile Trading",
    "updated_at": "Jul 18, 2026",
    "chequable": {
      "type": "SalesInvoice",
      "id": 44
    }
  }
}
DELETE /banking/cheques/{cheque} permission: cheque-delete

Soft-delete a cheque (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Cheque deleted successfully",
  "data": null
}
POST /banking/cheques/{cheque}/cash permission: cheque-update

Mark a cheque as cashed/deposited — moves the amount into the linked cash or bank account.

Request Body

application/json
{
  "deposit_type": "bank",
  "bank_account_id": 2,
  "date": "2026-07-25"
}

Response 200

200 OK
{
  "success": true,
  "message": "Cheque cashed successfully",
  "data": {
    "id": 19,
    "ref_number": "CHQ-000123",
    "amount": 30000,
    "type": "receivable",
    "status": "open",
    "cheque_date": "Jul 25, 2026",
    "description": "Cheque from Blue Nile Trading",
    "updated_at": "Jul 18, 2026",
    "chequable": {
      "type": "SalesInvoice",
      "id": 44
    }
  }
}
GET /banking/cheques/statistics/overview permission: cheque-read

Summary counts and totals of receivable/payable cheques by status.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "open_receivable": 45000,
    "open_payable": 12000,
    "overdue_count": 2
  }
}

Notifications — prefix /notifications

GET /notifications permission: notification-read

List the current user's notifications, newest first.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "notifications": [
      {
        "id": "9c4e2f1a-91b2-4b3c-8f10-000000000001",
        "type": "App\Modules\Common\Notifications\ChequeDueNotification",
        "data": {
          "type": "cheque_due",
          "cheque_id": 19,
          "ref_number": "CHQ-000123",
          "amount": 30000,
          "due_date": "2026-07-25",
          "message": "Cheque CHQ-000123 of 30000 is due on 2026-07-25."
        },
        "read_at": null,
        "created_at": "2026-07-18T09:00:00.000000Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 15,
      "total": 42,
      "last_page": 3
    }
  }
}
PATCH /notifications/read-all permission: notification-update

Mark every notification as read.

Response 200

200 OK
{
  "success": true,
  "message": "Marked as read",
  "data": {
    "unread_count": 0
  }
}
GET /notifications/unread-count permission: notification-read

Unread notification count, for the header bell badge.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "unread_count": 7
  }
}
DELETE /notifications/{id} permission: notification-delete

Delete a notification.

Response 200

200 OK
{
  "success": true,
  "message": "Deleted successfully",
  "data": null
}
PATCH /notifications/{id}/read permission: notification-update

Mark a single notification as read.

Response 200

200 OK
{
  "success": true,
  "message": "Marked as read",
  "data": {
    "unread_count": 6
  }
}
GET /notifications/settings permission: notification_setting-read

List configured notification rules (e.g. cheque-due reminders).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "settings": [
      {
        "id": 3,
        "key": "cheque_due_reminder",
        "enabled": true,
        "roles": [
          "owner"
        ],
        "days_before": 3
      }
    ]
  }
}
POST /notifications/settings permission: notification_setting-create

Create a notification rule.

Request Body

application/json
{
  "key": "cheque_due_reminder",
  "enabled": true,
  "roles": [
    "owner"
  ],
  "days_before": 3
}

Response 200

200 OK
{
  "success": true,
  "message": "Created successfully",
  "data": {
    "id": 3,
    "key": "cheque_due_reminder"
  }
}
GET /notifications/settings/{setting} permission: notification_setting-read

Get a single notification rule by key.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 3,
    "key": "cheque_due_reminder",
    "enabled": true
  }
}
PUT /notifications/settings/{setting} permission: notification_setting-update

Update a notification rule.

Request Body

application/json
{
  "enabled": false,
  "days_before": 5
}

Response 200

200 OK
{
  "success": true,
  "message": "Updated successfully",
  "data": null
}
DELETE /notifications/settings/{setting} permission: notification_setting-delete

Delete a notification rule.

Response 200

200 OK
{
  "success": true,
  "message": "Deleted successfully",
  "data": null
}

Company Profile & Settings

GET /company-profile permission: company_profile-read

Get the tenant's company profile (name, TIN, logo, contact info) shown on invoices.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 1,
    "logo": "https://.../logos/company1.png",
    "company_name": "Elilta Trading PLC",
    "tin_number": "0012345678",
    "receipt_code": "RC-00981",
    "address": "Bole Road, Addis Ababa",
    "phone": "0911909090",
    "email": "info@elilta.com",
    "created_at": "Jul 18, 2026"
  }
}
POST /company-profile permission: company_profile-create

Create the company profile (first-time setup).

Request Body

application/json
{
  "company_name": "Elilta Trading PLC",
  "tin_number": "0012345678",
  "receipt_code": "RC-00981",
  "address": "Bole Road, Addis Ababa",
  "phone": "0911909090",
  "email": "info@elilta.com"
}

Response 201

201 OK
{
  "success": true,
  "message": "Created successfully",
  "data": {
    "id": 1,
    "logo": "https://.../logos/company1.png",
    "company_name": "Elilta Trading PLC",
    "tin_number": "0012345678",
    "receipt_code": "RC-00981",
    "address": "Bole Road, Addis Ababa",
    "phone": "0911909090",
    "email": "info@elilta.com",
    "created_at": "Jul 18, 2026"
  }
}
PUT /company-profile permission: company_profile-update

Update the company profile.

Request Body

application/json
{
  "company_name": "Elilta Trading PLC",
  "tin_number": "0012345678",
  "receipt_code": "RC-00981",
  "address": "Bole Road, Addis Ababa",
  "phone": "0911909090",
  "email": "info@elilta.com"
}

Response 200

200 OK
{
  "success": true,
  "message": "Updated successfully",
  "data": {
    "id": 1,
    "logo": "https://.../logos/company1.png",
    "company_name": "Elilta Trading PLC",
    "tin_number": "0012345678",
    "receipt_code": "RC-00981",
    "address": "Bole Road, Addis Ababa",
    "phone": "0911909090",
    "email": "info@elilta.com",
    "created_at": "Jul 18, 2026"
  }
}
DELETE /company-profile/logo permission: company_profile-update

Remove the uploaded company logo.

Response 200

200 OK
{
  "success": true,
  "message": "Logo removed successfully",
  "data": null
}
GET /settings/general permission: setting_general-read

Get tenant-wide general settings (currency, negative-stock policy, which optional modules are enabled).

Response 200

200 OK
{
  "payments": {
    "enable_passcode": true,
    "business_currency": "ETB",
    "tin_number": true,
    "stop_sales_on_negative_stock": false,
    "estimate_quotation": true,
    "proforma_invoices": true,
    "sales_purchase_order": true,
    "other_income": true,
    "stock_transfer": true
  }
}
Response is wrapped in a "payments" key — a naming holdover in the backend, not billing-related.
POST /settings/general/update permission: setting_general-update

Update general settings — accepts any subset of the boolean fields above.

Request Body

application/json
{
  "business_currency": "ETB",
  "stop_sales_on_negative_stock": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Settings updated successfully",
  "data": null
}
GET /settings/transaction permission: setting_transaction-read

Get transaction-form behavior settings (tax display, quick entry, discount rules, etc.).

Response 200

200 OK
{
  "payments": {
    "invoice_bill_number": true,
    "transaction_wise_tax": true,
    "transaction_wise_discount": true,
    "due_dates_and_payment_terms": true,
    "billing_type": true
  }
}
GET /settings/transaction/prefix permission: setting_transaction_prefix-read

Get the configured document-number prefixes (invoices, orders, estimates, etc.).

Response 200

200 OK
{
  "transactions": {
    "id": 1,
    "firm": "ETL",
    "sales": "INV-",
    "purchase_order": "PO-",
    "estimate": "EST-",
    "proforma_invoice": "PF-",
    "payment_in": "PMT-"
  }
}
POST /settings/transaction/prefix/update permission: setting_transaction_prefix-update

Update document-number prefixes.

Request Body

application/json
{
  "sales": "INV-",
  "purchase_order": "PO-"
}

Response 200

200 OK
{
  "success": true,
  "message": "Prefixes updated successfully",
  "data": null
}
POST /settings/transaction/update permission: setting_transaction-update

Update transaction-form behavior settings — accepts any subset of the boolean fields above.

Request Body

application/json
{
  "transaction_wise_tax": true,
  "quick_entry": false
}

Response 200

200 OK
{
  "success": true,
  "message": "Settings updated successfully",
  "data": null
}

Dashboard Analytics — prefix /analytics

Error Handling

Errors always use the same envelope shape (success: false, message, optional errors map) with the appropriate HTTP status code.

CodeMeaningCommon cause
200OKSuccess
201CreatedResource created
400Bad RequestMalformed/inconsistent request (e.g. business-rule conflict)
401UnauthenticatedMissing/invalid/revoked Bearer token
403ForbiddenAuthenticated, but missing the required permission (or a protected resource, e.g. the Owner role)
404Not FoundResource id doesn't exist (or belongs to another tenant)
422Validation FailedSee the errors map for per-field messages
429Too Many RequestsRate limit hit on OTP/auth-check endpoints
500Server ErrorUnexpected failure — message includes detail in development

Roles & Permissions

Every tenant gets exactly two starting roles. Permissions follow a {module}-{create|read|update|delete} naming convention (e.g. item-create, sales_invoice-read) and are scoped to the tenant's business type.

Owner role id 1 · protected

Unrestricted — every permission for the tenant's business type. Creator-only: cannot be assigned to another user, cannot be edited or deleted.

Employee role id 2 · seeded default

Ships with a minimal read-only baseline (own profile, reports, parties, notifications, company profile). Fully customizable per-tenant via PUT /auth/roles/2 — its name is protected, but permissions are not.

Additional custom roles can be created via POST /auth/roles with any subset of the tenant's available permissions (GET /auth/permissions).

Docs/Product
Primary integration target

Product API Reference

Inventory, sales, purchases, warehousing, payments and reporting for Product-type tenants (service_type: "products"). All endpoints below live under the /product prefix unless noted. Haven't set up auth yet? Start on the Home page.

305

Product Endpoints

11

Modules

36

Report Endpoints

Soft-delete + restore everywhere

Pattern note: nearly every resource below follows the same shape — GET (list, paginated + filterable), POST (create), GET /{id} (show), PUT/PATCH /{id} (update), DELETE /{id} (soft-delete), POST /{id}/restore, DELETE /{id}/force (permanent), and GET /{id}/history (audit trail). Learning one module gets you 80% of the rest for free.

Dashboard & Analytics

Powers the Product dashboard's summary cards and charts. Shared infrastructure (prefix /analytics), not /product-prefixed.

GET /analytics/dashboard permission: report-read

Top-level dashboard summary — sales/purchase totals, receivables/payables, recent activity.

200 OK
{
  "success": true,
  "data": {
    "total_sales": 184500.00,
    "total_purchases": 92300.00,
    "receivables": 21450.00,
    "payables": 8900.00
  }
}

Items & Catalog

The core product catalog — items, per-item pricing, stock levels, stock adjustments and unit mappings.

Items — full reference

GET /product/items permission: item-read

List items. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Items retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/items permission: item-create

Create a new item.

Request Body

application/json
{
  "item_type": "goods",
  "name": "Cement 50kg",
  "sku_code": "CEM-50KG-001",
  "item_category_id": 3,
  "description": "OPC 42.5 grade cement bag",
  "hsn_code": "2523",
  "tin_applicable": true,
  "selling_price": 950,
  "purchase_price": 800,
  "mrp_price": 1000,
  "discount_type": "percentage",
  "discount_value": 5,
  "is_active": true,
  "units": [
    {
      "level": "primary",
      "unit_id": 3,
      "custom_factor": null
    }
  ],
  "warehouse_id": 1,
  "batch_no": "BATCH-2026-001",
  "opening_stock": 100,
  "reorder_level": 20,
  "allow_negative_stock": false
}

Response 201

201 OK
{
  "success": true,
  "message": "Item created successfully",
  "data": {
    "id": 101,
    "uuid": "b6e2c1a4-...",
    "item_type": "goods",
    "name": "Cement 50kg",
    "sku_code": "CEM-50KG-001",
    "barcode": "8901030875021",
    "description": "OPC 42.5 grade cement bag",
    "image": "https://.../item_images/cem.jpg",
    "hsn_code": "2523",
    "tin_applicable": true,
    "selling_price": 950,
    "purchase_price": 800,
    "mrp_price": 1000,
    "discount_type": "percentage",
    "discount_value": 5,
    "is_active": true,
    "notes": "Bulk discount available",
    "total_stock": 500,
    "available_stock": 480,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00",
    "category": {
      "id": 3,
      "name": "Building Materials",
      "code": "BLD"
    }
  }
}
GET /product/items/{item} permission: item-read

Get a single item by id.

Response 200

200 OK
{
  "success": true,
  "message": "Item retrieved successfully",
  "data": {
    "id": 101,
    "uuid": "b6e2c1a4-...",
    "item_type": "goods",
    "name": "Cement 50kg",
    "sku_code": "CEM-50KG-001",
    "barcode": "8901030875021",
    "description": "OPC 42.5 grade cement bag",
    "image": "https://.../item_images/cem.jpg",
    "hsn_code": "2523",
    "tin_applicable": true,
    "selling_price": 950,
    "purchase_price": 800,
    "mrp_price": 1000,
    "discount_type": "percentage",
    "discount_value": 5,
    "is_active": true,
    "notes": "Bulk discount available",
    "total_stock": 500,
    "available_stock": 480,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00",
    "category": {
      "id": 3,
      "name": "Building Materials",
      "code": "BLD"
    }
  }
}
PUT /product/items/{item} permission: item-update

Update an existing item.

Request Body

application/json
{
  "item_type": "goods",
  "name": "Cement 50kg",
  "sku_code": "CEM-50KG-001",
  "item_category_id": 3,
  "description": "OPC 42.5 grade cement bag",
  "hsn_code": "2523",
  "tin_applicable": true,
  "selling_price": 950,
  "purchase_price": 800,
  "mrp_price": 1000,
  "discount_type": "percentage",
  "discount_value": 5,
  "is_active": true,
  "units": [
    {
      "level": "primary",
      "unit_id": 3,
      "custom_factor": null
    }
  ],
  "warehouse_id": 1,
  "batch_no": "BATCH-2026-001",
  "opening_stock": 100,
  "reorder_level": 20,
  "allow_negative_stock": false
}

Response 200

200 OK
{
  "success": true,
  "message": "Item updated successfully",
  "data": {
    "id": 101,
    "uuid": "b6e2c1a4-...",
    "item_type": "goods",
    "name": "Cement 50kg",
    "sku_code": "CEM-50KG-001",
    "barcode": "8901030875021",
    "description": "OPC 42.5 grade cement bag",
    "image": "https://.../item_images/cem.jpg",
    "hsn_code": "2523",
    "tin_applicable": true,
    "selling_price": 950,
    "purchase_price": 800,
    "mrp_price": 1000,
    "discount_type": "percentage",
    "discount_value": 5,
    "is_active": true,
    "notes": "Bulk discount available",
    "total_stock": 500,
    "available_stock": 480,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00",
    "category": {
      "id": 3,
      "name": "Building Materials",
      "code": "BLD"
    }
  }
}
DELETE /product/items/{item} permission: item-delete

Soft-delete a item (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Item deleted successfully",
  "data": null
}
GET /product/items/{item}/transactions permission: item-read

List items. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Items retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
GET /product/items/autocomplete permission: item-read

Lightweight id+name list for autocomplete/typeahead item pickers.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001"
      }
    ]
  }
}
GET /product/items/filter permission: item-read

Advanced filtered item list (category, price range, stock status).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  }
}
GET /product/items/search permission: item-read

Full-text item search by name, SKU, or barcode.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  }
}
GET /product/items/{item}/transactions permission: item-read

Ledger transactions involving this item.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "transactions": [
      {
        "id": 512,
        "transaction_no": "TXN-2026-000452",
        "transaction_date": "2026-07-18 10:30:00",
        "amount": 9025,
        "type": "sale",
        "direction": "in",
        "description": "Payment received for INV-2026-0042",
        "reference_number": "RCPT-2026-0033",
        "status": "completed",
        "party_balance_before": 21525.75,
        "party_balance_after": 12500.75,
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "display_name": "Blue Nile Trading"
        }
      }
    ]
  }
}

Item Prices & Stocks

PUT /product/item-prices/{item_price} permission: item_price-update

Update an existing item price.

Request Body

application/json
{
  "price_type": "selling",
  "amount": 950,
  "currency": "Birr",
  "effective_from": "2026-07-01",
  "effective_to": null,
  "is_active": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Item Price updated successfully",
  "data": {
    "id": 21,
    "uuid": "d8b1c3f7-...",
    "price_type": "selling",
    "amount": 950,
    "currency": "Birr",
    "effective_from": "2026-07-01",
    "effective_to": null,
    "is_active": true,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/item-prices/{item_price} permission: item_price-delete

Soft-delete a item price (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Item Price deleted successfully",
  "data": null
}
GET /product/items/{item}/prices permission: item_price-read

Price list (selling/purchase/MRP, with effective date ranges) for this item.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "prices": [
      {
        "id": 21,
        "uuid": "d8b1c3f7-...",
        "price_type": "selling",
        "amount": 950,
        "currency": "Birr",
        "effective_from": "2026-07-01",
        "effective_to": null,
        "is_active": true,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  }
}
POST /product/items/{item}/prices permission: item_price-create

Add a price entry for this item.

Request Body

application/json
{
  "price_type": "selling",
  "amount": 950,
  "currency": "Birr",
  "effective_from": "2026-07-01",
  "effective_to": null,
  "is_active": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Price added successfully",
  "data": {
    "id": 21,
    "uuid": "d8b1c3f7-...",
    "price_type": "selling",
    "amount": 950,
    "currency": "Birr",
    "effective_from": "2026-07-01",
    "effective_to": null,
    "is_active": true,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/items/{item}/prices/history permission: item_price-read

Price change history for this item.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "history": []
  }
}
PUT /product/item-stocks/{item_stock} permission: item_stock-update

Update an existing item stock.

Request Body

application/json
{
  "warehouse_id": 1,
  "current_stock": 240,
  "reserved_stock": 0,
  "opening_stock": 100,
  "opening_stock_value": 78000,
  "average_cost": 780,
  "last_purchase_price": 800,
  "reorder_level": 20,
  "allow_negative_stock": false,
  "valuation_method": "fifo"
}

Response 200

200 OK
{
  "success": true,
  "message": "Item Stock updated successfully",
  "data": {
    "id": 55,
    "uuid": "b6f9a1d5-...",
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "batch_no": "BATCH-2026-001",
    "current_stock": 240,
    "reserved_stock": 10,
    "available_stock": 230,
    "opening_stock": 100,
    "average_cost": 780,
    "last_purchase_price": 800,
    "reorder_level": 20,
    "allow_negative_stock": false,
    "valuation_method": "fifo",
    "selling_price": 950,
    "purchase_price": 800,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/item-stocks/{item_stock} permission: item_stock-delete

Soft-delete a item stock (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Item Stock deleted successfully",
  "data": null
}
GET /product/items/{item}/stocks permission: item_stock-read

Stock levels for this item, by warehouse and batch.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "stocks": [
      {
        "id": 55,
        "uuid": "b6f9a1d5-...",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        },
        "batch_no": "BATCH-2026-001",
        "current_stock": 240,
        "reserved_stock": 10,
        "available_stock": 230,
        "opening_stock": 100,
        "average_cost": 780,
        "last_purchase_price": 800,
        "reorder_level": 20,
        "allow_negative_stock": false,
        "valuation_method": "fifo",
        "selling_price": 950,
        "purchase_price": 800,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  }
}
POST /product/items/{item}/stocks permission: item_stock-create

Create a stock record for this item in a warehouse (e.g. opening stock for a new batch).

Request Body

application/json
{
  "warehouse_id": 1,
  "current_stock": 240,
  "reserved_stock": 0,
  "opening_stock": 100,
  "opening_stock_value": 78000,
  "average_cost": 780,
  "last_purchase_price": 800,
  "reorder_level": 20,
  "allow_negative_stock": false,
  "valuation_method": "fifo"
}

Response 201

201 OK
{
  "success": true,
  "message": "Stock created successfully",
  "data": {
    "id": 55,
    "uuid": "b6f9a1d5-...",
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "batch_no": "BATCH-2026-001",
    "current_stock": 240,
    "reserved_stock": 10,
    "available_stock": 230,
    "opening_stock": 100,
    "average_cost": 780,
    "last_purchase_price": 800,
    "reorder_level": 20,
    "allow_negative_stock": false,
    "valuation_method": "fifo",
    "selling_price": 950,
    "purchase_price": 800,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/items/{item}/stocks/history permission: item_stock-read

Stock-level change history for this item.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "history": []
  }
}
GET /product/items/{item}/stock-adjustments permission: stock_adjustment-read

List stock adjustments. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Stock Adjustments retrieved successfully",
  "data": {
    "adjustments": [
      {
        "id": 88,
        "uuid": "c7a0b2e6-...",
        "item_stock": {
          "id": 55,
          "current_stock": 240,
          "available_stock": 230
        },
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        },
        "adjustment_type": "add",
        "quantity": 50,
        "unit_cost": 780,
        "total_value": 39000,
        "stock_before": 190,
        "stock_after": 240,
        "batch_no": "BATCH-2026-001",
        "reason": "stock_correction",
        "notes": "Physical count adjustment",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/items/{item}/stock-adjustments permission: stock_adjustment-create

Create a new stock adjustment.

Request Body

application/json
{
  "warehouse_id": 1,
  "adjustment_type": "add",
  "quantity": 50,
  "unit_cost": 780,
  "reason": "stock_correction",
  "batch_no": "BATCH-2026-001",
  "notes": "Physical count adjustment"
}

Response 201

201 OK
{
  "success": true,
  "message": "Stock Adjustment created successfully",
  "data": {
    "id": 88,
    "uuid": "c7a0b2e6-...",
    "item_stock": {
      "id": 55,
      "current_stock": 240,
      "available_stock": 230
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "adjustment_type": "add",
    "quantity": 50,
    "unit_cost": 780,
    "total_value": 39000,
    "stock_before": 190,
    "stock_after": 240,
    "batch_no": "BATCH-2026-001",
    "reason": "stock_correction",
    "notes": "Physical count adjustment",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/items/{item}/stock-adjustments/{stock_adjustment} permission: stock_adjustment-read

Get a single stock adjustment by id.

Response 200

200 OK
{
  "success": true,
  "message": "Stock Adjustment retrieved successfully",
  "data": {
    "id": 88,
    "uuid": "c7a0b2e6-...",
    "item_stock": {
      "id": 55,
      "current_stock": 240,
      "available_stock": 230
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "adjustment_type": "add",
    "quantity": 50,
    "unit_cost": 780,
    "total_value": 39000,
    "stock_before": 190,
    "stock_after": 240,
    "batch_no": "BATCH-2026-001",
    "reason": "stock_correction",
    "notes": "Physical count adjustment",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/items/{item}/stock-adjustments/{stock_adjustment} permission: stock_adjustment-update

Update an existing stock adjustment.

Request Body

application/json
{
  "warehouse_id": 1,
  "adjustment_type": "add",
  "quantity": 50,
  "unit_cost": 780,
  "reason": "stock_correction",
  "batch_no": "BATCH-2026-001",
  "notes": "Physical count adjustment"
}

Response 200

200 OK
{
  "success": true,
  "message": "Stock Adjustment updated successfully",
  "data": {
    "id": 88,
    "uuid": "c7a0b2e6-...",
    "item_stock": {
      "id": 55,
      "current_stock": 240,
      "available_stock": 230
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "adjustment_type": "add",
    "quantity": 50,
    "unit_cost": 780,
    "total_value": 39000,
    "stock_before": 190,
    "stock_after": 240,
    "batch_no": "BATCH-2026-001",
    "reason": "stock_correction",
    "notes": "Physical count adjustment",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/items/{item}/stock-adjustments/{stock_adjustment} permission: stock_adjustment-delete

Soft-delete a stock adjustment (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Stock Adjustment deleted successfully",
  "data": null
}
GET /product/items/{item}/settings permission: item_setting-read

List items. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Items retrieved successfully",
  "data": {
    "settings": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/items/{item}/settings permission: item_setting-create

Create a new item.

Request Body

application/json
{
  "item_type": "goods",
  "name": "Cement 50kg",
  "sku_code": "CEM-50KG-001",
  "item_category_id": 3,
  "description": "OPC 42.5 grade cement bag",
  "hsn_code": "2523",
  "tin_applicable": true,
  "selling_price": 950,
  "purchase_price": 800,
  "mrp_price": 1000,
  "discount_type": "percentage",
  "discount_value": 5,
  "is_active": true,
  "units": [
    {
      "level": "primary",
      "unit_id": 3,
      "custom_factor": null
    }
  ],
  "warehouse_id": 1,
  "batch_no": "BATCH-2026-001",
  "opening_stock": 100,
  "reorder_level": 20,
  "allow_negative_stock": false
}

Response 201

201 OK
{
  "success": true,
  "message": "Item created successfully",
  "data": {
    "id": 101,
    "uuid": "b6e2c1a4-...",
    "item_type": "goods",
    "name": "Cement 50kg",
    "sku_code": "CEM-50KG-001",
    "barcode": "8901030875021",
    "description": "OPC 42.5 grade cement bag",
    "image": "https://.../item_images/cem.jpg",
    "hsn_code": "2523",
    "tin_applicable": true,
    "selling_price": 950,
    "purchase_price": 800,
    "mrp_price": 1000,
    "discount_type": "percentage",
    "discount_value": 5,
    "is_active": true,
    "notes": "Bulk discount available",
    "total_stock": 500,
    "available_stock": 480,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00",
    "category": {
      "id": 3,
      "name": "Building Materials",
      "code": "BLD"
    }
  }
}
PUT /product/items/{item}/settings permission: item-*

Per-item settings (tax applicability, stock-maintenance flags, etc.).

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}
DELETE /product/items/{item}/settings permission: item-*

Per-item settings (tax applicability, stock-maintenance flags, etc.).

Response 200

200 OK
{
  "success": true,
  "message": "Operation completed successfully",
  "data": // shape depends on the specific action — see description above
}
GET /product/items/{item}/unit-mappings permission: item_unit_mapping-read

List unit mappings. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Unit Mappings retrieved successfully",
  "data": {
    "mappings": [
      {
        "id": 12,
        "uuid": "f4d7e9b3-...",
        "item_id": 101,
        "unit_id": 5,
        "level": "secondary",
        "custom_factor": 12,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "unit": {
          "id": 5,
          "name": "Box",
          "short_code": "box"
        },
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/items/{item}/unit-mappings permission: item_unit_mapping-create

Create a new unit mapping.

Request Body

application/json
{
  "unit_id": 5,
  "level": "secondary",
  "custom_factor": 12
}

Response 201

201 OK
{
  "success": true,
  "message": "Unit Mapping created successfully",
  "data": {
    "id": 12,
    "uuid": "f4d7e9b3-...",
    "item_id": 101,
    "unit_id": 5,
    "level": "secondary",
    "custom_factor": 12,
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "unit": {
      "id": 5,
      "name": "Box",
      "short_code": "box"
    },
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}

Item Categories

Hierarchical categorization for items. Prefix /product/item-categories.

GET /product/item-categories permission: item_category-read

List item categorys. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Item Categorys retrieved successfully",
  "data": {
    "categories": [
      {
        "id": 3,
        "uuid": "c1a4b6e2-...",
        "name": "Building Materials",
        "description": "Cement, rebar, and other construction materials",
        "is_active": true,
        "items_count": 24,
        "status": "active",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/item-categories permission: item_category-create

Create a new item category.

Request Body

application/json
{
  "name": "Building Materials",
  "description": "Cement, rebar, and other construction materials",
  "is_active": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Item Category created successfully",
  "data": {
    "id": 3,
    "uuid": "c1a4b6e2-...",
    "name": "Building Materials",
    "description": "Cement, rebar, and other construction materials",
    "is_active": true,
    "items_count": 24,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/item-categories/{item_category} permission: item_category-read

Get a single item category by id.

Response 200

200 OK
{
  "success": true,
  "message": "Item Category retrieved successfully",
  "data": {
    "id": 3,
    "uuid": "c1a4b6e2-...",
    "name": "Building Materials",
    "description": "Cement, rebar, and other construction materials",
    "is_active": true,
    "items_count": 24,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/item-categories/{item_category} permission: item_category-update

Update an existing item category.

Request Body

application/json
{
  "name": "Building Materials",
  "description": "Cement, rebar, and other construction materials",
  "is_active": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Item Category updated successfully",
  "data": {
    "id": 3,
    "uuid": "c1a4b6e2-...",
    "name": "Building Materials",
    "description": "Cement, rebar, and other construction materials",
    "is_active": true,
    "items_count": 24,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/item-categories/{item_category} permission: item_category-delete

Soft-delete a item category (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Item Category deleted successfully",
  "data": null
}

Units & Conversions

Units of measure, unit-to-unit conversion rules, and per-item unit mappings (e.g. selling both by "box" and "piece").

Item Units /product/item-units

GET /product/item-units permission: item_unit-read

List item units. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Item Units retrieved successfully",
  "data": {
    "units": [
      {
        "id": 3,
        "uuid": "d2b5c7f1-...",
        "name": "Kilogram",
        "short_code": "kg",
        "is_base": true,
        "is_active": true,
        "items_count": 41,
        "status": "active",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/item-units permission: item_unit-create

Create a new item unit.

Request Body

application/json
{
  "name": "Kilogram",
  "short_code": "kg",
  "is_base": true,
  "is_active": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Item Unit created successfully",
  "data": {
    "id": 3,
    "uuid": "d2b5c7f1-...",
    "name": "Kilogram",
    "short_code": "kg",
    "is_base": true,
    "is_active": true,
    "items_count": 41,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/item-units/{item_unit} permission: item_unit-read

Get a single item unit by id.

Response 200

200 OK
{
  "success": true,
  "message": "Item Unit retrieved successfully",
  "data": {
    "id": 3,
    "uuid": "d2b5c7f1-...",
    "name": "Kilogram",
    "short_code": "kg",
    "is_base": true,
    "is_active": true,
    "items_count": 41,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/item-units/{item_unit} permission: item_unit-update

Update an existing item unit.

Request Body

application/json
{
  "name": "Kilogram",
  "short_code": "kg",
  "is_base": true,
  "is_active": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Item Unit updated successfully",
  "data": {
    "id": 3,
    "uuid": "d2b5c7f1-...",
    "name": "Kilogram",
    "short_code": "kg",
    "is_base": true,
    "is_active": true,
    "items_count": 41,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/item-units/{item_unit} permission: item_unit-delete

Soft-delete a item unit (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Item Unit deleted successfully",
  "data": null
}
GET /product/item-units/base-units permission: item_unit-read

List base (root, non-derived) units.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "units": [
      {
        "id": 3,
        "uuid": "d2b5c7f1-...",
        "name": "Kilogram",
        "short_code": "kg",
        "is_base": true,
        "is_active": true,
        "items_count": 41,
        "status": "active",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  }
}
POST /product/item-units/bulk-with-conversions permission: item_unit-create

Create multiple units together with their conversion rules in a single call.

Request Body

application/json
{
  "units": [
    {
      "name": "Box",
      "short_code": "box",
      "conversions": [
        {
          "to_unit_id": 3,
          "factor": 12
        }
      ]
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Units created successfully",
  "data": null
}
GET /product/item-units/conversion-path permission: item_unit-read

Resolve the conversion path/factor between two units.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "from_unit_id": 5,
    "to_unit_id": 3,
    "factor": 12
  }
}
GET /product/item-units/hierarchy permission: item_unit-read

Full unit hierarchy tree (base units with their derived conversions).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "units": [
      {
        "id": 3,
        "uuid": "d2b5c7f1-...",
        "name": "Kilogram",
        "short_code": "kg",
        "is_base": true,
        "is_active": true,
        "items_count": 41,
        "status": "active",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "children": []
      }
    ]
  }
}
GET /product/item-units/validation permission: item_unit-read

Validate a proposed unit configuration before saving.

Response 200

200 OK
{
  "success": true,
  "message": "Valid",
  "data": null
}

Unit Conversions /product/unit-conversions

GET /product/unit-conversions permission: unit_conversion-read

List unit conversions. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Unit Conversions retrieved successfully",
  "data": {
    "conversions": [
      {
        "id": 7,
        "uuid": "e3c6d8a2-...",
        "from_unit_id": 3,
        "to_unit_id": 4,
        "factor": 1000,
        "is_bidirectional": true,
        "from_unit": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "to_unit": {
          "id": 4,
          "name": "Gram",
          "short_code": "g"
        },
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/unit-conversions permission: unit_conversion-create

Create a new unit conversion.

Request Body

application/json
{
  "from_unit_id": 3,
  "to_unit_id": 4,
  "factor": 1000,
  "is_bidirectional": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Unit Conversion created successfully",
  "data": {
    "id": 7,
    "uuid": "e3c6d8a2-...",
    "from_unit_id": 3,
    "to_unit_id": 4,
    "factor": 1000,
    "is_bidirectional": true,
    "from_unit": {
      "id": 3,
      "name": "Kilogram",
      "short_code": "kg"
    },
    "to_unit": {
      "id": 4,
      "name": "Gram",
      "short_code": "g"
    },
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/unit-conversions/{unit_conversion} permission: unit_conversion-read

Get a single unit conversion by id.

Response 200

200 OK
{
  "success": true,
  "message": "Unit Conversion retrieved successfully",
  "data": {
    "id": 7,
    "uuid": "e3c6d8a2-...",
    "from_unit_id": 3,
    "to_unit_id": 4,
    "factor": 1000,
    "is_bidirectional": true,
    "from_unit": {
      "id": 3,
      "name": "Kilogram",
      "short_code": "kg"
    },
    "to_unit": {
      "id": 4,
      "name": "Gram",
      "short_code": "g"
    },
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/unit-conversions/{unit_conversion} permission: unit_conversion-update

Update an existing unit conversion.

Request Body

application/json
{
  "from_unit_id": 3,
  "to_unit_id": 4,
  "factor": 1000,
  "is_bidirectional": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Unit Conversion updated successfully",
  "data": {
    "id": 7,
    "uuid": "e3c6d8a2-...",
    "from_unit_id": 3,
    "to_unit_id": 4,
    "factor": 1000,
    "is_bidirectional": true,
    "from_unit": {
      "id": 3,
      "name": "Kilogram",
      "short_code": "kg"
    },
    "to_unit": {
      "id": 4,
      "name": "Gram",
      "short_code": "g"
    },
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/unit-conversions/{unit_conversion} permission: unit_conversion-delete

Soft-delete a unit conversion (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Unit Conversion deleted successfully",
  "data": null
}

Unit Mappings /product/unit-mappings

PUT /product/unit-mappings/{item_unit_mapping} permission: item_unit_mapping-update

Update an existing unit mapping.

Request Body

application/json
{
  "unit_id": 5,
  "level": "secondary",
  "custom_factor": 12
}

Response 200

200 OK
{
  "success": true,
  "message": "Unit Mapping updated successfully",
  "data": {
    "id": 12,
    "uuid": "f4d7e9b3-...",
    "item_id": 101,
    "unit_id": 5,
    "level": "secondary",
    "custom_factor": 12,
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "unit": {
      "id": 5,
      "name": "Box",
      "short_code": "box"
    },
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/unit-mappings/{item_unit_mapping} permission: item_unit_mapping-delete

Soft-delete a unit mapping (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Unit Mapping deleted successfully",
  "data": null
}

Warehouses & Stock

Physical/logical storage locations, and moving stock between them.

Warehouses /product/warehouses

GET /product/warehouses permission: warehouse-read

List warehouses. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Warehouses retrieved successfully",
  "data": {
    "warehouses": [
      {
        "id": 1,
        "uuid": "a5e8f0c4-...",
        "name": "Addis Ababa Main Warehouse",
        "code": "WH-AA-01",
        "address": "Merkato Industrial Zone",
        "city": "Addis Ababa",
        "country": "Ethiopia",
        "contact_person": "Dawit Mekonnen",
        "phone": "0911909090",
        "email": "warehouse@elilta.com",
        "is_default": true,
        "is_active": true,
        "status": "active",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/warehouses permission: warehouse-create

Create a new warehouse.

Request Body

application/json
{
  "name": "Addis Ababa Main Warehouse",
  "code": "WH-AA-01",
  "address": "Merkato Industrial Zone",
  "city": "Addis Ababa",
  "country": "Ethiopia",
  "contact_person": "Dawit Mekonnen",
  "phone": "0911909090",
  "email": "warehouse@elilta.com",
  "is_default": false,
  "is_active": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Warehouse created successfully",
  "data": {
    "id": 1,
    "uuid": "a5e8f0c4-...",
    "name": "Addis Ababa Main Warehouse",
    "code": "WH-AA-01",
    "address": "Merkato Industrial Zone",
    "city": "Addis Ababa",
    "country": "Ethiopia",
    "contact_person": "Dawit Mekonnen",
    "phone": "0911909090",
    "email": "warehouse@elilta.com",
    "is_default": true,
    "is_active": true,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/warehouses/{warehouse} permission: warehouse-read

Get a single warehouse by id.

Response 200

200 OK
{
  "success": true,
  "message": "Warehouse retrieved successfully",
  "data": {
    "id": 1,
    "uuid": "a5e8f0c4-...",
    "name": "Addis Ababa Main Warehouse",
    "code": "WH-AA-01",
    "address": "Merkato Industrial Zone",
    "city": "Addis Ababa",
    "country": "Ethiopia",
    "contact_person": "Dawit Mekonnen",
    "phone": "0911909090",
    "email": "warehouse@elilta.com",
    "is_default": true,
    "is_active": true,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/warehouses/{warehouse} permission: warehouse-update

Update an existing warehouse.

Request Body

application/json
{
  "name": "Addis Ababa Main Warehouse",
  "code": "WH-AA-01",
  "address": "Merkato Industrial Zone",
  "city": "Addis Ababa",
  "country": "Ethiopia",
  "contact_person": "Dawit Mekonnen",
  "phone": "0911909090",
  "email": "warehouse@elilta.com",
  "is_default": false,
  "is_active": true
}

Response 200

200 OK
{
  "success": true,
  "message": "Warehouse updated successfully",
  "data": {
    "id": 1,
    "uuid": "a5e8f0c4-...",
    "name": "Addis Ababa Main Warehouse",
    "code": "WH-AA-01",
    "address": "Merkato Industrial Zone",
    "city": "Addis Ababa",
    "country": "Ethiopia",
    "contact_person": "Dawit Mekonnen",
    "phone": "0911909090",
    "email": "warehouse@elilta.com",
    "is_default": true,
    "is_active": true,
    "status": "active",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/warehouses/{warehouse} permission: warehouse-delete

Soft-delete a warehouse (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Warehouse deleted successfully",
  "data": null
}
GET /product/warehouses/items/{warehouse} permission: warehouse-read

Items currently stocked in this warehouse.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "items": [
      {
        "id": 101,
        "uuid": "b6e2c1a4-...",
        "item_type": "goods",
        "name": "Cement 50kg",
        "sku_code": "CEM-50KG-001",
        "barcode": "8901030875021",
        "description": "OPC 42.5 grade cement bag",
        "image": "https://.../item_images/cem.jpg",
        "hsn_code": "2523",
        "tin_applicable": true,
        "selling_price": 950,
        "purchase_price": 800,
        "mrp_price": 1000,
        "discount_type": "percentage",
        "discount_value": 5,
        "is_active": true,
        "notes": "Bulk discount available",
        "total_stock": 500,
        "available_stock": 480,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00",
        "category": {
          "id": 3,
          "name": "Building Materials",
          "code": "BLD"
        }
      }
    ]
  }
}

Stock Transfers /product/stock-transfers

GET /product/stock-transfers permission: stock_transfer-read

List stock transfers. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Stock Transfers retrieved successfully",
  "data": {
    "transfers": [
      {
        "id": 14,
        "uuid": "e9c2d4a8-...",
        "from_warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        },
        "to_warehouse": {
          "id": 2,
          "name": "Adama Branch Warehouse",
          "code": "WH-AD-01"
        },
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "transfer_date": "2026-07-18",
        "quantity": 30,
        "notes": "Restocking branch",
        "can_process": true,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/stock-transfers permission: stock_transfer-create

Create a new stock transfer.

Request Body

application/json
{
  "from_warehouse_id": 1,
  "to_warehouse_id": 2,
  "item_id": 101,
  "transfer_date": "2026-07-18",
  "quantity": 30,
  "from_batch_no": "BATCH-2026-001",
  "to_batch_no": "BATCH-2026-001",
  "notes": "Restocking branch"
}

Response 201

201 OK
{
  "success": true,
  "message": "Stock Transfer created successfully",
  "data": {
    "id": 14,
    "uuid": "e9c2d4a8-...",
    "from_warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "to_warehouse": {
      "id": 2,
      "name": "Adama Branch Warehouse",
      "code": "WH-AD-01"
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "transfer_date": "2026-07-18",
    "quantity": 30,
    "notes": "Restocking branch",
    "can_process": true,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/stock-transfers/{stock_transfer} permission: stock_transfer-read

Get a single stock transfer by id.

Response 200

200 OK
{
  "success": true,
  "message": "Stock Transfer retrieved successfully",
  "data": {
    "id": 14,
    "uuid": "e9c2d4a8-...",
    "from_warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "to_warehouse": {
      "id": 2,
      "name": "Adama Branch Warehouse",
      "code": "WH-AD-01"
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "transfer_date": "2026-07-18",
    "quantity": 30,
    "notes": "Restocking branch",
    "can_process": true,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/stock-transfers/{stock_transfer} permission: stock_transfer-update

Update an existing stock transfer.

Request Body

application/json
{
  "from_warehouse_id": 1,
  "to_warehouse_id": 2,
  "item_id": 101,
  "transfer_date": "2026-07-18",
  "quantity": 30,
  "from_batch_no": "BATCH-2026-001",
  "to_batch_no": "BATCH-2026-001",
  "notes": "Restocking branch"
}

Response 200

200 OK
{
  "success": true,
  "message": "Stock Transfer updated successfully",
  "data": {
    "id": 14,
    "uuid": "e9c2d4a8-...",
    "from_warehouse": {
      "id": 1,
      "name": "Addis Ababa Main Warehouse",
      "code": "WH-AA-01"
    },
    "to_warehouse": {
      "id": 2,
      "name": "Adama Branch Warehouse",
      "code": "WH-AD-01"
    },
    "item": {
      "id": 101,
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001"
    },
    "transfer_date": "2026-07-18",
    "quantity": 30,
    "notes": "Restocking branch",
    "can_process": true,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/stock-transfers/{stock_transfer} permission: stock_transfer-delete

Soft-delete a stock transfer (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Stock Transfer deleted successfully",
  "data": null
}

Parties (Customers / Suppliers)

Parties are Common/shared infrastructure, not Product-specific — full reference lives on the Home page → CRM / Parties (prefix /crm). Listed here because every Sales/Purchase transaction requires a party_id.

Sales

Sales Invoices, Sales Orders, Proformas and Sales Returns. Prefix /product/sales and /product/sales-returns.

Sales Invoices & Orders — full reference

GET /product/sales permission: sales_invoice-read

List sales invoices. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Invoices retrieved successfully",
  "data": {
    "sales": [
      {
        "id": 44,
        "is_credit": true,
        "invoice_no": "INV-2026-0042",
        "invoice_date": "2026-07-18",
        "due_date": "2026-08-17",
        "invoice_type": "standard",
        "status": "unpaid",
        "sub_total": 9500,
        "discount_total": 475,
        "tax_total": 0,
        "shipping_total": 0,
        "round_off": 0,
        "grand_total": 9025,
        "grand_total_in_words": "Nine Thousand Twenty Five Birr",
        "paid_amount": 0,
        "balance_amount": 9025,
        "notes": "Delivered to site",
        "reference_no": "PO-BN-118",
        "currency": "ETB",
        "payment_type": "cash",
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        },
        "lines": [
          {
            "id": 1,
            "qty": 10,
            "rate": 950,
            "discount_type": "percentage",
            "discount_value": 5,
            "line_discount_total": 475,
            "line_sub_total": 9025,
            "tax_total": 0,
            "line_total": 9025,
            "batch_no": "BATCH-2026-001",
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/sales permission: sales_invoice-create

Create a new sales invoice.

Request Body

application/json
{
  "is_credit": true,
  "invoice_no": "INV-2026-0042",
  "party_id": 15,
  "invoice_date": "2026-07-18",
  "due_date": "2026-08-17",
  "invoice_type": "standard",
  "reference_no": "PO-BN-118",
  "currency": "ETB",
  "payment_type": "cash",
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "paid_amount": 0,
  "advance": false,
  "balance_amount": 9025,
  "lines": [
    {
      "item_id": 101,
      "uom_id": 3,
      "quantity": 10,
      "rate": 950,
      "sub_total": 9500,
      "discount_value": 475,
      "tax_amount": 0,
      "total_amount": 9025,
      "batch_no": "BATCH-2026-001",
      "warehouse_id": 1
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Sales Invoice created successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
GET /product/sales/{salesInvoice} permission: sales_invoice-read

Get a single sales invoice by id.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Invoice retrieved successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
PUT /product/sales/{salesInvoice} permission: sales_invoice-update

Update an existing sales invoice.

Request Body

application/json
{
  "is_credit": true,
  "invoice_no": "INV-2026-0042",
  "party_id": 15,
  "invoice_date": "2026-07-18",
  "due_date": "2026-08-17",
  "invoice_type": "standard",
  "reference_no": "PO-BN-118",
  "currency": "ETB",
  "payment_type": "cash",
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "paid_amount": 0,
  "advance": false,
  "balance_amount": 9025,
  "lines": [
    {
      "item_id": 101,
      "uom_id": 3,
      "quantity": 10,
      "rate": 950,
      "sub_total": 9500,
      "discount_value": 475,
      "tax_amount": 0,
      "total_amount": 9025,
      "batch_no": "BATCH-2026-001",
      "warehouse_id": 1
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Sales Invoice updated successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
DELETE /product/sales/{salesInvoice} permission: sales_invoice-delete

Soft-delete a sales invoice (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Sales Invoice deleted successfully",
  "data": null
}
GET /product/sales/by-number/{invoice_no} permission: sales_invoice-read

Look up a sales invoice by its invoice number.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
POST /product/sales/create-return permission: sales_return-create

Create a return against an existing sales invoice (no invoice id required in the path — invoice is identified in the body).

Request Body

application/json
{
  "reference_invoice_id": 44,
  "lines": [
    {
      "item_id": 101,
      "quantity": 1
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Return created successfully",
  "data": {
    "id": 3,
    "is_credit": true,
    "return_no": "SR-2026-0003",
    "return_date": "2026-07-18",
    "invoice_date": "2026-07-10",
    "invoice_no": "INV-2026-0038",
    "sub_total": 950,
    "discount_total": 0,
    "tax_total": 0,
    "grand_total": 950,
    "paid_amount": 950,
    "balance_amount": 0,
    "payment_type": "cash",
    "reason": "damaged goods",
    "total_refund": 950,
    "status": "confirmed",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 1,
        "unit": "kg",
        "price_per_unit": 950,
        "amount": 950,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
POST /product/sales/revert-return/{returnId} permission: sales_return-update

Revert a previously confirmed sales return, restoring the original invoice balance.

Response 200

200 OK
{
  "success": true,
  "message": "Return reverted successfully",
  "data": null
}
GET /product/sales/search-by-number permission: sales_invoice-read

Search sales invoices by number (query param q).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "sales": [
      {
        "id": 44,
        "is_credit": true,
        "invoice_no": "INV-2026-0042",
        "invoice_date": "2026-07-18",
        "due_date": "2026-08-17",
        "invoice_type": "standard",
        "status": "unpaid",
        "sub_total": 9500,
        "discount_total": 475,
        "tax_total": 0,
        "shipping_total": 0,
        "round_off": 0,
        "grand_total": 9025,
        "grand_total_in_words": "Nine Thousand Twenty Five Birr",
        "paid_amount": 0,
        "balance_amount": 9025,
        "notes": "Delivered to site",
        "reference_no": "PO-BN-118",
        "currency": "ETB",
        "payment_type": "cash",
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        },
        "lines": [
          {
            "id": 1,
            "qty": 10,
            "rate": 950,
            "discount_type": "percentage",
            "discount_value": 5,
            "line_discount_total": 475,
            "line_sub_total": 9025,
            "tax_total": 0,
            "line_total": 9025,
            "batch_no": "BATCH-2026-001",
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ]
      }
    ]
  }
}
GET /product/sales/{invoiceNo}/available-return-lines permission: sales_return-read

Line items on this invoice still eligible to be returned.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "lines": [
      {
        "item_id": 101,
        "returnable_quantity": 9
      }
    ]
  }
}
POST /product/sales/{salesInvoice}/confirm permission: sales_invoice-update

Confirm/finalize this invoice — locks it and applies stock & ledger effects. Cannot be undone.

Response 200

200 OK
{
  "success": true,
  "message": "Invoice confirmed successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
GET /product/sales/{salesInvoice}/payments permission: sales_payment-read

Payments recorded against this invoice.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "payments": [
      {
        "id": 33,
        "party_id": 15,
        "is_opening_balance": false,
        "payment_type": "bank",
        "bank_account_id": 2,
        "receipt_number": "RCPT-2026-0033",
        "payment_date": "2026-07-18",
        "description": "Payment for INV-2026-0042",
        "received_amount": 9025,
        "status": "used",
        "attachments": [],
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        }
      }
    ]
  }
}
POST /product/sales/{salesInvoice}/payments permission: sales_payment-create

Record a payment directly against this invoice.

Request Body

application/json
{
  "amount": 9025,
  "payment_type": "cash",
  "payment_date": "2026-07-18"
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment recorded successfully",
  "data": {
    "id": 33,
    "party_id": 15,
    "is_opening_balance": false,
    "payment_type": "bank",
    "bank_account_id": 2,
    "receipt_number": "RCPT-2026-0033",
    "payment_date": "2026-07-18",
    "description": "Payment for INV-2026-0042",
    "received_amount": 9025,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    }
  }
}
GET /product/sales/orders permission: sales_order-read

List sales orders. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Orders retrieved successfully",
  "data": {
    "orders": [
      {
        "id": 9,
        "order_date": "2026-07-18",
        "order_number": "SO-2026-0009",
        "due_date": "2026-07-28",
        "payment_type": "cash",
        "reference_no": "PO-BN-118",
        "advance_amount": 0,
        "balance_due": 9025,
        "sub_total": 9500,
        "discount_total": 475,
        "tax_total": 0,
        "round_off": 0,
        "grand_total": 9025,
        "paid_amount": 0,
        "balance_amount": 9025,
        "net_amount": 9025,
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        },
        "lines": [
          {
            "id": 1,
            "qty": 10,
            "rate": 950,
            "discount_type": "percentage",
            "discount_value": 5,
            "line_discount_total": 475,
            "line_sub_total": 9025,
            "tax_total": 0,
            "line_total": 9025,
            "batch_no": "BATCH-2026-001",
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/sales/orders permission: sales_order-create

Create a new sales order.

Request Body

application/json
{
  "party_id": 15,
  "reference_no": "PO-BN-118",
  "order_date": "2026-07-18",
  "due_date": "2026-07-28",
  "payment_type": "cash",
  "advance_amount": 0,
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "balance_due": 9025,
  "lines": [
    {
      "item_id": 101,
      "quantity": 10,
      "rate": 950,
      "discount_type": "percentage",
      "discount_value": 475,
      "sub_total": 9500,
      "tax_total": 0,
      "total_amount": 9025,
      "uom_id": 3,
      "warehouse_id": 1
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Sales Order created successfully",
  "data": {
    "id": 9,
    "order_date": "2026-07-18",
    "order_number": "SO-2026-0009",
    "due_date": "2026-07-28",
    "payment_type": "cash",
    "reference_no": "PO-BN-118",
    "advance_amount": 0,
    "balance_due": 9025,
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "paid_amount": 0,
    "balance_amount": 9025,
    "net_amount": 9025,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
GET /product/sales/orders/{sales_order} permission: sales_order-read

Get a single sales order by id.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Order retrieved successfully",
  "data": {
    "id": 9,
    "order_date": "2026-07-18",
    "order_number": "SO-2026-0009",
    "due_date": "2026-07-28",
    "payment_type": "cash",
    "reference_no": "PO-BN-118",
    "advance_amount": 0,
    "balance_due": 9025,
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "paid_amount": 0,
    "balance_amount": 9025,
    "net_amount": 9025,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
PUT /product/sales/orders/{sales_order} permission: sales_order-update

Update an existing sales order.

Request Body

application/json
{
  "party_id": 15,
  "reference_no": "PO-BN-118",
  "order_date": "2026-07-18",
  "due_date": "2026-07-28",
  "payment_type": "cash",
  "advance_amount": 0,
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "balance_due": 9025,
  "lines": [
    {
      "item_id": 101,
      "quantity": 10,
      "rate": 950,
      "discount_type": "percentage",
      "discount_value": 475,
      "sub_total": 9500,
      "tax_total": 0,
      "total_amount": 9025,
      "uom_id": 3,
      "warehouse_id": 1
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Sales Order updated successfully",
  "data": {
    "id": 9,
    "order_date": "2026-07-18",
    "order_number": "SO-2026-0009",
    "due_date": "2026-07-28",
    "payment_type": "cash",
    "reference_no": "PO-BN-118",
    "advance_amount": 0,
    "balance_due": 9025,
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "paid_amount": 0,
    "balance_amount": 9025,
    "net_amount": 9025,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
DELETE /product/sales/orders/{sales_order} permission: sales_order-delete

Soft-delete a sales order (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Sales Order deleted successfully",
  "data": null
}
POST /product/sales/orders/bulk-convert-to-invoice permission: sales_invoice-create

Bulk-convert multiple selected sales orders into invoices in one call.

Request Body

application/json
{
  "order_ids": [
    9,
    10,
    11
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Orders converted successfully",
  "data": null
}
POST /product/sales/orders/{sales_order}/convert-to-invoice permission: sales_invoice-create

Convert this sales order into a sales invoice.

Response 201

201 OK
{
  "success": true,
  "message": "Converted to invoice successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
GET /product/sales/proformas permission: sales_proforma-read

List sales proformas. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Proformas retrieved successfully",
  "data": {
    "proformas": [
      {
        "id": 6,
        "invoice_number": "PF-2026-0006",
        "invoice_date": "2026-07-18",
        "sub_total": 9500,
        "discount_total": 475,
        "tax_total": 0,
        "round_off": 0,
        "grand_total": 9025,
        "validity_days": 30,
        "validity_expiry_date": "2026-08-17",
        "validity_status": "valid",
        "is_valid": true,
        "days_remaining": 25,
        "is_converted": false,
        "can_convert": true,
        "net_amount": 9025,
        "payment_status": "unpaid",
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        },
        "lines": [
          {
            "id": 1,
            "qty": 10,
            "rate": 950,
            "discount_type": "percentage",
            "discount_value": 5,
            "line_discount_total": 475,
            "line_sub_total": 9025,
            "tax_total": 0,
            "line_total": 9025,
            "batch_no": "BATCH-2026-001",
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/sales/proformas permission: sales_proforma-create

Create a new sales proforma.

Request Body

application/json
{
  "party_id": 15,
  "invoice_date": "2026-07-18",
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "validity_days": 30,
  "lines": [
    {
      "item_id": 101,
      "quantity": 10,
      "rate": 950,
      "discount_type": "percentage",
      "discount_value": 475,
      "sub_total": 9500,
      "tax_total": 0,
      "total_amount": 9025,
      "uom_id": 3,
      "warehouse_id": 1
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Sales Proforma created successfully",
  "data": {
    "id": 6,
    "invoice_number": "PF-2026-0006",
    "invoice_date": "2026-07-18",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "validity_days": 30,
    "validity_expiry_date": "2026-08-17",
    "validity_status": "valid",
    "is_valid": true,
    "days_remaining": 25,
    "is_converted": false,
    "can_convert": true,
    "net_amount": 9025,
    "payment_status": "unpaid",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
GET /product/sales/proformas/{salesProforma} permission: sales_proforma-read

Get a single sales proforma by id.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Proforma retrieved successfully",
  "data": {
    "id": 6,
    "invoice_number": "PF-2026-0006",
    "invoice_date": "2026-07-18",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "validity_days": 30,
    "validity_expiry_date": "2026-08-17",
    "validity_status": "valid",
    "is_valid": true,
    "days_remaining": 25,
    "is_converted": false,
    "can_convert": true,
    "net_amount": 9025,
    "payment_status": "unpaid",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
PUT /product/sales/proformas/{salesProforma} permission: sales_proforma-update

Update an existing sales proforma.

Request Body

application/json
{
  "party_id": 15,
  "invoice_date": "2026-07-18",
  "sub_total": 9500,
  "discount_total": 475,
  "tax_total": 0,
  "round_off": 0,
  "grand_total": 9025,
  "validity_days": 30,
  "lines": [
    {
      "item_id": 101,
      "quantity": 10,
      "rate": 950,
      "discount_type": "percentage",
      "discount_value": 475,
      "sub_total": 9500,
      "tax_total": 0,
      "total_amount": 9025,
      "uom_id": 3,
      "warehouse_id": 1
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Sales Proforma updated successfully",
  "data": {
    "id": 6,
    "invoice_number": "PF-2026-0006",
    "invoice_date": "2026-07-18",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "validity_days": 30,
    "validity_expiry_date": "2026-08-17",
    "validity_status": "valid",
    "is_valid": true,
    "days_remaining": 25,
    "is_converted": false,
    "can_convert": true,
    "net_amount": 9025,
    "payment_status": "unpaid",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
DELETE /product/sales/proformas/{salesProforma} permission: sales_proforma-delete

Soft-delete a sales proforma (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Sales Proforma deleted successfully",
  "data": null
}
POST /product/sales/proformas/{salesProforma}/convert-to-sales permission: sales_invoice-create

Convert this proforma into a sales invoice.

Response 201

201 OK
{
  "success": true,
  "message": "Converted to invoice successfully",
  "data": {
    "id": 44,
    "is_credit": true,
    "invoice_no": "INV-2026-0042",
    "invoice_date": "2026-07-18",
    "due_date": "2026-08-17",
    "invoice_type": "standard",
    "status": "unpaid",
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "shipping_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "grand_total_in_words": "Nine Thousand Twenty Five Birr",
    "paid_amount": 0,
    "balance_amount": 9025,
    "notes": "Delivered to site",
    "reference_no": "PO-BN-118",
    "currency": "ETB",
    "payment_type": "cash",
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}
POST /product/sales/proformas/{salesProforma}/convert-to-sales-order permission: sales_order-create

Convert this proforma into a sales order.

Response 201

201 OK
{
  "success": true,
  "message": "Converted to order successfully",
  "data": {
    "id": 9,
    "order_date": "2026-07-18",
    "order_number": "SO-2026-0009",
    "due_date": "2026-07-28",
    "payment_type": "cash",
    "reference_no": "PO-BN-118",
    "advance_amount": 0,
    "balance_due": 9025,
    "sub_total": 9500,
    "discount_total": 475,
    "tax_total": 0,
    "round_off": 0,
    "grand_total": 9025,
    "paid_amount": 0,
    "balance_amount": 9025,
    "net_amount": 9025,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "qty": 10,
        "rate": 950,
        "discount_type": "percentage",
        "discount_value": 5,
        "line_discount_total": 475,
        "line_sub_total": 9025,
        "tax_total": 0,
        "line_total": 9025,
        "batch_no": "BATCH-2026-001",
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ]
  }
}

Sales Returns

GET /product/sales-returns permission: sales_return-read

List sales returns. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Returns retrieved successfully",
  "data": {
    "returns": [
      {
        "id": 3,
        "is_credit": true,
        "return_no": "SR-2026-0003",
        "return_date": "2026-07-18",
        "invoice_date": "2026-07-10",
        "invoice_no": "INV-2026-0038",
        "sub_total": 950,
        "discount_total": 0,
        "tax_total": 0,
        "grand_total": 950,
        "paid_amount": 950,
        "balance_amount": 0,
        "payment_type": "cash",
        "reason": "damaged goods",
        "total_refund": 950,
        "status": "confirmed",
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        },
        "lines": [
          {
            "id": 1,
            "item_id": 101,
            "quantity": 1,
            "unit": "kg",
            "price_per_unit": 950,
            "amount": 950,
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/sales-returns permission: sales_return-create

Create a new sales return.

Request Body

application/json
{
  "party_id": 15,
  "paid_amount": 950,
  "reference_invoice_id": 38,
  "return_date": "2026-07-18",
  "invoice_date": "2026-07-10",
  "invoice_no": "INV-2026-0038",
  "payment_type": "cash",
  "sub_total": 950,
  "discount_total": 0,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 950,
  "balance_amount": 0,
  "reason": "damaged goods",
  "total_refund": 950,
  "lines": [
    {
      "item_id": 101,
      "warehouse_id": 1,
      "batch_no": "BATCH-2026-001",
      "quantity": 1,
      "unit": "kg",
      "price_per_unit": 950,
      "amount": 950
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Sales Return created successfully",
  "data": {
    "id": 3,
    "is_credit": true,
    "return_no": "SR-2026-0003",
    "return_date": "2026-07-18",
    "invoice_date": "2026-07-10",
    "invoice_no": "INV-2026-0038",
    "sub_total": 950,
    "discount_total": 0,
    "tax_total": 0,
    "grand_total": 950,
    "paid_amount": 950,
    "balance_amount": 0,
    "payment_type": "cash",
    "reason": "damaged goods",
    "total_refund": 950,
    "status": "confirmed",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 1,
        "unit": "kg",
        "price_per_unit": 950,
        "amount": 950,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
GET /product/sales-returns/{sales_return} permission: sales_return-read

Get a single sales return by id.

Response 200

200 OK
{
  "success": true,
  "message": "Sales Return retrieved successfully",
  "data": {
    "id": 3,
    "is_credit": true,
    "return_no": "SR-2026-0003",
    "return_date": "2026-07-18",
    "invoice_date": "2026-07-10",
    "invoice_no": "INV-2026-0038",
    "sub_total": 950,
    "discount_total": 0,
    "tax_total": 0,
    "grand_total": 950,
    "paid_amount": 950,
    "balance_amount": 0,
    "payment_type": "cash",
    "reason": "damaged goods",
    "total_refund": 950,
    "status": "confirmed",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 1,
        "unit": "kg",
        "price_per_unit": 950,
        "amount": 950,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
PUT /product/sales-returns/{sales_return} permission: sales_return-update

Update an existing sales return.

Request Body

application/json
{
  "party_id": 15,
  "paid_amount": 950,
  "reference_invoice_id": 38,
  "return_date": "2026-07-18",
  "invoice_date": "2026-07-10",
  "invoice_no": "INV-2026-0038",
  "payment_type": "cash",
  "sub_total": 950,
  "discount_total": 0,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 950,
  "balance_amount": 0,
  "reason": "damaged goods",
  "total_refund": 950,
  "lines": [
    {
      "item_id": 101,
      "warehouse_id": 1,
      "batch_no": "BATCH-2026-001",
      "quantity": 1,
      "unit": "kg",
      "price_per_unit": 950,
      "amount": 950
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Sales Return updated successfully",
  "data": {
    "id": 3,
    "is_credit": true,
    "return_no": "SR-2026-0003",
    "return_date": "2026-07-18",
    "invoice_date": "2026-07-10",
    "invoice_no": "INV-2026-0038",
    "sub_total": 950,
    "discount_total": 0,
    "tax_total": 0,
    "grand_total": 950,
    "paid_amount": 950,
    "balance_amount": 0,
    "payment_type": "cash",
    "reason": "damaged goods",
    "total_refund": 950,
    "status": "confirmed",
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 1,
        "unit": "kg",
        "price_per_unit": 950,
        "amount": 950,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
DELETE /product/sales-returns/{sales_return} permission: sales_return-delete

Soft-delete a sales return (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Sales Return deleted successfully",
  "data": null
}

Purchases

Purchase Bills, Purchase Orders (with GRNs) and Purchase Returns. Prefix /product/purchases, /product/purchase-orders, /product/purchase-returns.

Purchase Bills — full reference

GET /product/purchases permission: purchase_invoice-read

List purchase invoices. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Invoices retrieved successfully",
  "data": {
    "purchases": [
      {
        "id": 91,
        "is_credit": true,
        "party_id": 22,
        "phone_no": "0911909090",
        "bill_no": "BILL-2026-0011",
        "bill_date": "2026-07-18",
        "payment_type": "cheque",
        "cheque_reference_no": "CHQ-000123",
        "discount": 0,
        "tax": 0,
        "total": 78000,
        "paid_amount": 0,
        "payable_amount": 78000,
        "status": "confirmed",
        "balance_amount": 78000,
        "grand_total": 78000,
        "description": "Monthly cement restock",
        "created_at": "2026-07-18 10:30:00",
        "lines": [
          {
            "id": 1,
            "quantity": 100,
            "unit": "kg",
            "price_per_unit": 780,
            "amount": 78000,
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "batch_no": "BATCH-2026-001",
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ],
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC",
          "phone": "0922334455"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/purchases permission: purchase_invoice-create

Create a new purchase invoice.

Request Body

application/json
{
  "is_credit": true,
  "advance": false,
  "party_id": 22,
  "phone_no": "0911909090",
  "bill_no": "BILL-2026-0011",
  "bill_date": "2026-07-18",
  "payment_type": "cheque",
  "tax": 0,
  "total": 78000,
  "paid_amount": 0,
  "description": "Monthly cement restock",
  "lines": [
    {
      "item_id": 101,
      "quantity": 100,
      "unit": "kg",
      "price_per_unit": 780,
      "amount": 78000,
      "batch_no": "BATCH-2026-001",
      "warehouse_id": 1
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Purchase Invoice created successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
GET /product/purchases/{purchase} permission: purchase_invoice-read

Get a single purchase invoice by id.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Invoice retrieved successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
PUT /product/purchases/{purchase} permission: purchase_invoice-update

Update an existing purchase invoice.

Request Body

application/json
{
  "is_credit": true,
  "advance": false,
  "party_id": 22,
  "phone_no": "0911909090",
  "bill_no": "BILL-2026-0011",
  "bill_date": "2026-07-18",
  "payment_type": "cheque",
  "tax": 0,
  "total": 78000,
  "paid_amount": 0,
  "description": "Monthly cement restock",
  "lines": [
    {
      "item_id": 101,
      "quantity": 100,
      "unit": "kg",
      "price_per_unit": 780,
      "amount": 78000,
      "batch_no": "BATCH-2026-001",
      "warehouse_id": 1
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Invoice updated successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
DELETE /product/purchases/{purchase} permission: purchase_invoice-delete

Soft-delete a purchase invoice (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Invoice deleted successfully",
  "data": null
}
POST /product/purchases/auto-match-invoice permission: purchase_invoice-read

Attempt to auto-match an uploaded bill against an existing purchase order by number/amount.

Request Body

application/json
{
  "bill_no": "BILL-2026-0011",
  "total": 78000
}

Response 200

200 OK
{
  "success": true,
  "message": "Match found",
  "data": {
    "purchase_order_id": 17
  }
}
GET /product/purchases/by-number/{bill_no} permission: purchase_invoice-read

Look up a purchase bill by its bill number.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
POST /product/purchases/create-return permission: purchase_return-create

Create a return against an existing purchase bill.

Request Body

application/json
{
  "purchase_invoice_id": 91,
  "lines": [
    {
      "item_id": 101,
      "quantity": 10
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Return created successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
GET /product/purchases/returns/{return} permission: purchase_return-read

Get a single purchase return by id.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
POST /product/purchases/returns/{return}/confirm permission: purchase_return-update

Confirm/finalize this purchase return — locks it and applies stock & ledger effects.

Response 200

200 OK
{
  "success": true,
  "message": "Return confirmed successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
POST /product/purchases/revert-return/{returnId} permission: purchase_return-update

Revert a previously confirmed purchase return, restoring the original bill balance.

Response 200

200 OK
{
  "success": true,
  "message": "Return reverted successfully",
  "data": null
}
GET /product/purchases/search-by-number permission: purchase_invoice-read

Search purchase bills by number (query param q).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "purchases": [
      {
        "id": 91,
        "is_credit": true,
        "party_id": 22,
        "phone_no": "0911909090",
        "bill_no": "BILL-2026-0011",
        "bill_date": "2026-07-18",
        "payment_type": "cheque",
        "cheque_reference_no": "CHQ-000123",
        "discount": 0,
        "tax": 0,
        "total": 78000,
        "paid_amount": 0,
        "payable_amount": 78000,
        "status": "confirmed",
        "balance_amount": 78000,
        "grand_total": 78000,
        "description": "Monthly cement restock",
        "created_at": "2026-07-18 10:30:00",
        "lines": [
          {
            "id": 1,
            "quantity": 100,
            "unit": "kg",
            "price_per_unit": 780,
            "amount": 78000,
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "batch_no": "BATCH-2026-001",
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ],
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC",
          "phone": "0922334455"
        }
      }
    ]
  }
}
POST /product/purchases/validate-credit-limit permission: purchase_invoice-create

Check a proposed purchase against the supplier's credit limit before committing.

Request Body

application/json
{
  "party_id": 22,
  "amount": 78000
}

Response 200

200 OK
{
  "success": true,
  "message": "Within credit limit",
  "data": {
    "within_limit": true,
    "available_credit": 122000
  }
}
GET /product/purchases/{billNo}/available-return-lines permission: purchase_return-read

Line items on this bill still eligible to be returned.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "lines": [
      {
        "item_id": 101,
        "returnable_quantity": 90
      }
    ]
  }
}
GET /product/purchases/{purchase}/attachments permission: purchase_invoice-read

File attachments on this purchase bill.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "attachments": []
  }
}
POST /product/purchases/{purchase}/attachments permission: purchase_invoice-update

Upload a file attachment to this purchase bill (multipart/form-data).

Response 201

201 OK
{
  "success": true,
  "message": "Attachment uploaded successfully",
  "data": null
}
POST /product/purchases/{purchase}/confirm permission: purchase_invoice-update

Confirm/finalize this purchase bill — locks it and applies stock & ledger effects.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase confirmed successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
POST /product/purchases/{purchase}/create-bill-from-po permission: purchase_invoice-create

Generate a purchase bill from this approved purchase order.

Response 201

201 OK
{
  "success": true,
  "message": "Bill created successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
POST /product/purchases/{purchase}/lines permission: purchase_invoice-update

Add a line item to this purchase bill.

Request Body

application/json
{
  "item_id": 101,
  "quantity": 10,
  "price_per_unit": 780,
  "warehouse_id": 1
}

Response 201

201 OK
{
  "success": true,
  "message": "Line added successfully",
  "data": null
}
PUT /product/purchases/{purchase}/lines/{line} permission: purchase_invoice-update

Update a line item on this purchase bill.

Request Body

application/json
{
  "quantity": 12,
  "price_per_unit": 780
}

Response 200

200 OK
{
  "success": true,
  "message": "Line updated successfully",
  "data": null
}
DELETE /product/purchases/{purchase}/lines/{line} permission: purchase_invoice-update

Remove a line item from this purchase bill.

Response 200

200 OK
{
  "success": true,
  "message": "Line removed successfully",
  "data": null
}
POST /product/purchases/{purchase}/payments permission: purchase_payment-create

Record a payment directly against this purchase bill.

Request Body

application/json
{
  "amount": 78000,
  "payment_type": "cheque"
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment recorded successfully",
  "data": {
    "id": 18,
    "party_id": 22,
    "payment_type": "cheque",
    "receipt_number": "RCPT-OUT-2026-0018",
    "payment_date": "2026-07-18",
    "description": "Payment for BILL-2026-0011",
    "paid_amount": 78000,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    }
  }
}
GET /product/purchases/{purchase}/payments permission: purchase_payment-read

Payments recorded against this purchase bill.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "payments": [
      {
        "id": 18,
        "party_id": 22,
        "payment_type": "cheque",
        "receipt_number": "RCPT-OUT-2026-0018",
        "payment_date": "2026-07-18",
        "description": "Payment for BILL-2026-0011",
        "paid_amount": 78000,
        "status": "used",
        "attachments": [],
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC"
        }
      }
    ]
  }
}

Purchase Orders

GET /product/purchase-orders permission: purchase_order-read

List purchase orders. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Orders retrieved successfully",
  "data": {
    "orders": [
      {
        "id": 17,
        "party_id": 22,
        "phone_no": "0911909090",
        "po_no": "PO-2026-0007",
        "po_date": "2026-07-18",
        "status": "issued",
        "is_converted": false,
        "discount": 0,
        "tax": 0,
        "total": 78000,
        "advance_amount": 0,
        "payable_amount": 78000,
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC"
        },
        "lines": [
          {
            "id": 1,
            "quantity": 100,
            "unit": "kg",
            "price_per_unit": 780,
            "amount": 78000,
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            },
            "batch_no": "BATCH-2026-001",
            "uom": {
              "id": 3,
              "name": "Kilogram",
              "short_code": "kg"
            },
            "warehouse": {
              "id": 1,
              "name": "Addis Ababa Main Warehouse",
              "code": "WH-AA-01"
            }
          }
        ],
        "grns": []
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/purchase-orders permission: purchase_order-create

Create a new purchase order.

Request Body

application/json
{
  "party_id": 22,
  "phone_no": "0911909090",
  "po_no": "PO-2026-0007",
  "payment_type": "cheque",
  "po_date": "2026-07-18",
  "tax": 0,
  "total": 78000,
  "advance_amount": 0,
  "lines": [
    {
      "item_id": 101,
      "quantity": 100,
      "unit": "kg",
      "price_per_unit": 780,
      "warehouse_id": 1,
      "amount": 78000
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Purchase Order created successfully",
  "data": {
    "id": 17,
    "party_id": 22,
    "phone_no": "0911909090",
    "po_no": "PO-2026-0007",
    "po_date": "2026-07-18",
    "status": "issued",
    "is_converted": false,
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "advance_amount": 0,
    "payable_amount": 78000,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "grns": []
  }
}
GET /product/purchase-orders/{purchaseOrder} permission: purchase_order-read

Get a single purchase order by id.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Order retrieved successfully",
  "data": {
    "id": 17,
    "party_id": 22,
    "phone_no": "0911909090",
    "po_no": "PO-2026-0007",
    "po_date": "2026-07-18",
    "status": "issued",
    "is_converted": false,
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "advance_amount": 0,
    "payable_amount": 78000,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "grns": []
  }
}
PUT /product/purchase-orders/{purchaseOrder} permission: purchase_order-update

Update an existing purchase order.

Request Body

application/json
{
  "party_id": 22,
  "phone_no": "0911909090",
  "po_no": "PO-2026-0007",
  "payment_type": "cheque",
  "po_date": "2026-07-18",
  "tax": 0,
  "total": 78000,
  "advance_amount": 0,
  "lines": [
    {
      "item_id": 101,
      "quantity": 100,
      "unit": "kg",
      "price_per_unit": 780,
      "warehouse_id": 1,
      "amount": 78000
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Order updated successfully",
  "data": {
    "id": 17,
    "party_id": 22,
    "phone_no": "0911909090",
    "po_no": "PO-2026-0007",
    "po_date": "2026-07-18",
    "status": "issued",
    "is_converted": false,
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "advance_amount": 0,
    "payable_amount": 78000,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "grns": []
  }
}
DELETE /product/purchase-orders/{purchaseOrder} permission: purchase_order-delete

Soft-delete a purchase order (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Order deleted successfully",
  "data": null
}
POST /product/purchase-orders/{purchaseOrder}/convert-to-invoice permission: purchase_invoice-create

Convert this purchase order into a purchase bill.

Response 201

201 OK
{
  "success": true,
  "message": "Converted to invoice successfully",
  "data": {
    "id": 91,
    "is_credit": true,
    "party_id": 22,
    "phone_no": "0911909090",
    "bill_no": "BILL-2026-0011",
    "bill_date": "2026-07-18",
    "payment_type": "cheque",
    "cheque_reference_no": "CHQ-000123",
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "paid_amount": 0,
    "payable_amount": 78000,
    "status": "confirmed",
    "balance_amount": 78000,
    "grand_total": 78000,
    "description": "Monthly cement restock",
    "created_at": "2026-07-18 10:30:00",
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC",
      "phone": "0922334455"
    }
  }
}
GET /product/purchase-orders/{purchaseOrder}/grns permission: purchase_grn-read

List goods received notes. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Goods Received Notes retrieved successfully",
  "data": {
    "grns": [
      {
        "id": 4,
        "purchase_order_id": 17,
        "grn_no": "GRN-2026-0004",
        "grn_date": "2026-07-18",
        "status": "received",
        "remarks": "Full delivery received in good condition",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/purchase-orders/{purchaseOrder}/grns permission: purchase_grn-create

Create a new goods received note.

Request Body

application/json
{
  "grn_no": "GRN-2026-0004",
  "grn_date": "2026-07-18",
  "remarks": "Full delivery received in good condition"
}

Response 201

201 OK
{
  "success": true,
  "message": "Goods Received Note created successfully",
  "data": {
    "id": 4,
    "purchase_order_id": 17,
    "grn_no": "GRN-2026-0004",
    "grn_date": "2026-07-18",
    "status": "received",
    "remarks": "Full delivery received in good condition",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
POST /product/purchase-orders/{purchaseOrder}/issue permission: purchase_order-update

Issue this purchase order to the supplier — locks it for editing.

Response 200

200 OK
{
  "success": true,
  "message": "Order issued successfully",
  "data": {
    "id": 17,
    "party_id": 22,
    "phone_no": "0911909090",
    "po_no": "PO-2026-0007",
    "po_date": "2026-07-18",
    "status": "issued",
    "is_converted": false,
    "discount": 0,
    "tax": 0,
    "total": 78000,
    "advance_amount": 0,
    "payable_amount": 78000,
    "created_at": "2026-07-18 10:30:00",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "quantity": 100,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 78000,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        },
        "batch_no": "BATCH-2026-001",
        "uom": {
          "id": 3,
          "name": "Kilogram",
          "short_code": "kg"
        },
        "warehouse": {
          "id": 1,
          "name": "Addis Ababa Main Warehouse",
          "code": "WH-AA-01"
        }
      }
    ],
    "grns": []
  }
}
POST /product/purchase-orders/{purchaseOrder}/lines permission: purchase_order-update

Add a line item to this purchase order.

Request Body

application/json
{
  "item_id": 101,
  "quantity": 100,
  "price_per_unit": 780,
  "warehouse_id": 1
}

Response 201

201 OK
{
  "success": true,
  "message": "Line added successfully",
  "data": null
}
PUT /product/purchase-orders/{purchaseOrder}/lines/{poLine} permission: purchase_order-update

Update a line item on this purchase order.

Request Body

application/json
{
  "quantity": 120
}

Response 200

200 OK
{
  "success": true,
  "message": "Line updated successfully",
  "data": null
}
DELETE /product/purchase-orders/{purchaseOrder}/lines/{poLine} permission: purchase_order-update

Remove a line item from this purchase order.

Response 200

200 OK
{
  "success": true,
  "message": "Line removed successfully",
  "data": null
}

Purchase Returns

GET /product/purchase-returns permission: purchase_return-read

List purchase returns. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Returns retrieved successfully",
  "data": {
    "returns": [
      {
        "id": 5,
        "is_credit": true,
        "purchase_invoice_id": 91,
        "return_no": "PR-2026-0005",
        "return_date": "2026-07-18",
        "bill_date": "2026-07-18",
        "bill_no": "BILL-2026-0011",
        "sub_total": 7800,
        "grand_total": 7800,
        "received_amount": 7800,
        "balance_amount": 0,
        "payment_type": "cheque",
        "reason": "wrong item delivered",
        "total_refund": 7800,
        "status": "confirmed",
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC"
        },
        "lines": [
          {
            "id": 1,
            "item_id": 101,
            "quantity": 10,
            "unit": "kg",
            "price_per_unit": 780,
            "amount": 7800,
            "item": {
              "id": 101,
              "name": "Cement 50kg",
              "sku_code": "CEM-50KG-001"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/purchase-returns permission: purchase_return-create

Create a new purchase return.

Request Body

application/json
{
  "party_id": 22,
  "received_amount": 7800,
  "purchase_invoice_id": 91,
  "return_date": "2026-07-18",
  "bill_date": "2026-07-18",
  "bill_no": "BILL-2026-0011",
  "payment_type": "cheque",
  "sub_total": 7800,
  "discount_total": 0,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 7800,
  "balance_amount": 0,
  "reason": "wrong item delivered",
  "total_refund": 7800,
  "lines": [
    {
      "item_id": 101,
      "warehouse_id": 1,
      "batch_no": "BATCH-2026-001",
      "quantity": 10,
      "unit": "kg",
      "price_per_unit": 780,
      "amount": 7800
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Purchase Return created successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
GET /product/purchase-returns/{purchase_return} permission: purchase_return-read

Get a single purchase return by id.

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Return retrieved successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
PUT /product/purchase-returns/{purchase_return} permission: purchase_return-update

Update an existing purchase return.

Request Body

application/json
{
  "party_id": 22,
  "received_amount": 7800,
  "purchase_invoice_id": 91,
  "return_date": "2026-07-18",
  "bill_date": "2026-07-18",
  "bill_no": "BILL-2026-0011",
  "payment_type": "cheque",
  "sub_total": 7800,
  "discount_total": 0,
  "tax_total": 0,
  "shipping_total": 0,
  "round_off": 0,
  "grand_total": 7800,
  "balance_amount": 0,
  "reason": "wrong item delivered",
  "total_refund": 7800,
  "lines": [
    {
      "item_id": 101,
      "warehouse_id": 1,
      "batch_no": "BATCH-2026-001",
      "quantity": 10,
      "unit": "kg",
      "price_per_unit": 780,
      "amount": 7800
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Return updated successfully",
  "data": {
    "id": 5,
    "is_credit": true,
    "purchase_invoice_id": 91,
    "return_no": "PR-2026-0005",
    "return_date": "2026-07-18",
    "bill_date": "2026-07-18",
    "bill_no": "BILL-2026-0011",
    "sub_total": 7800,
    "grand_total": 7800,
    "received_amount": 7800,
    "balance_amount": 0,
    "payment_type": "cheque",
    "reason": "wrong item delivered",
    "total_refund": 7800,
    "status": "confirmed",
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    },
    "lines": [
      {
        "id": 1,
        "item_id": 101,
        "quantity": 10,
        "unit": "kg",
        "price_per_unit": 780,
        "amount": 7800,
        "item": {
          "id": 101,
          "name": "Cement 50kg",
          "sku_code": "CEM-50KG-001"
        }
      }
    ]
  }
}
DELETE /product/purchase-returns/{purchase_return} permission: purchase_return-delete

Soft-delete a purchase return (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Purchase Return deleted successfully",
  "data": null
}

Payments & Transactions

Recording money received against sales (Payment-In) and paid against purchases (Payment-Out), plus the unified ledger view.

Payment In /product/payment-ins

GET /product/payment-ins permission: payment_in-read

List payment ins. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Payment Ins retrieved successfully",
  "data": {
    "payment_ins": [
      {
        "id": 33,
        "party_id": 15,
        "is_opening_balance": false,
        "payment_type": "bank",
        "bank_account_id": 2,
        "receipt_number": "RCPT-2026-0033",
        "payment_date": "2026-07-18",
        "description": "Payment for INV-2026-0042",
        "received_amount": 9025,
        "status": "used",
        "attachments": [],
        "party": {
          "id": 15,
          "uuid": "9f2c1a3e-...",
          "party_type": "customer",
          "legal_name": "Blue Nile Trading PLC",
          "display_name": "Blue Nile Trading",
          "phone": "0911909090",
          "balance": 12500.75
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/payment-ins permission: payment_in-create

Create a new payment in.

Request Body

application/json
{
  "party_id": 15,
  "payment_type": "bank",
  "receipt_number": "RCPT-2026-0033",
  "payment_date": "2026-07-18",
  "bank_account_id": 2,
  "description": "Payment for INV-2026-0042",
  "received_amount": 9025,
  "allocations": [
    {
      "invoiceable_type": "App\Modules\Product\Models\SalesInvoice",
      "invoiceable_id": 44,
      "amount": 9025,
      "notes": "Full settlement"
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment In created successfully",
  "data": {
    "id": 33,
    "party_id": 15,
    "is_opening_balance": false,
    "payment_type": "bank",
    "bank_account_id": 2,
    "receipt_number": "RCPT-2026-0033",
    "payment_date": "2026-07-18",
    "description": "Payment for INV-2026-0042",
    "received_amount": 9025,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    }
  }
}
GET /product/payment-ins/{paymentIn} permission: payment_in-read

Get a single payment in by id.

Response 200

200 OK
{
  "success": true,
  "message": "Payment In retrieved successfully",
  "data": {
    "id": 33,
    "party_id": 15,
    "is_opening_balance": false,
    "payment_type": "bank",
    "bank_account_id": 2,
    "receipt_number": "RCPT-2026-0033",
    "payment_date": "2026-07-18",
    "description": "Payment for INV-2026-0042",
    "received_amount": 9025,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    }
  }
}
PUT /product/payment-ins/{paymentIn} permission: payment_in-update

Update an existing payment in.

Request Body

application/json
{
  "party_id": 15,
  "payment_type": "bank",
  "receipt_number": "RCPT-2026-0033",
  "payment_date": "2026-07-18",
  "bank_account_id": 2,
  "description": "Payment for INV-2026-0042",
  "received_amount": 9025,
  "allocations": [
    {
      "invoiceable_type": "App\Modules\Product\Models\SalesInvoice",
      "invoiceable_id": 44,
      "amount": 9025,
      "notes": "Full settlement"
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Payment In updated successfully",
  "data": {
    "id": 33,
    "party_id": 15,
    "is_opening_balance": false,
    "payment_type": "bank",
    "bank_account_id": 2,
    "receipt_number": "RCPT-2026-0033",
    "payment_date": "2026-07-18",
    "description": "Payment for INV-2026-0042",
    "received_amount": 9025,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "phone": "0911909090",
      "balance": 12500.75
    }
  }
}
DELETE /product/payment-ins/{paymentIn} permission: payment_in-delete

Soft-delete a payment in (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Payment In deleted successfully",
  "data": null
}

Payment Out /product/payment-outs

GET /product/payment-outs permission: payment_out-read

List payment outs. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Payment Outs retrieved successfully",
  "data": {
    "payment_outs": [
      {
        "id": 18,
        "party_id": 22,
        "payment_type": "cheque",
        "receipt_number": "RCPT-OUT-2026-0018",
        "payment_date": "2026-07-18",
        "description": "Payment for BILL-2026-0011",
        "paid_amount": 78000,
        "status": "used",
        "attachments": [],
        "party": {
          "id": 22,
          "display_name": "Ethio Cement Suppliers PLC"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/payment-outs permission: payment_out-create

Create a new payment out.

Request Body

application/json
{
  "party_id": 22,
  "payment_type": "cheque",
  "receipt_number": "RCPT-OUT-2026-0018",
  "payment_date": "2026-07-18",
  "description": "Payment for BILL-2026-0011",
  "paid_amount": 78000,
  "allocations": [
    {
      "invoiceable_type": "App\Modules\Product\Models\PurchaseInvoice",
      "invoiceable_id": 91,
      "amount": 78000,
      "notes": "Full settlement"
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment Out created successfully",
  "data": {
    "id": 18,
    "party_id": 22,
    "payment_type": "cheque",
    "receipt_number": "RCPT-OUT-2026-0018",
    "payment_date": "2026-07-18",
    "description": "Payment for BILL-2026-0011",
    "paid_amount": 78000,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    }
  }
}
GET /product/payment-outs/{payment_out} permission: payment_out-read

Get a single payment out by id.

Response 200

200 OK
{
  "success": true,
  "message": "Payment Out retrieved successfully",
  "data": {
    "id": 18,
    "party_id": 22,
    "payment_type": "cheque",
    "receipt_number": "RCPT-OUT-2026-0018",
    "payment_date": "2026-07-18",
    "description": "Payment for BILL-2026-0011",
    "paid_amount": 78000,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    }
  }
}
PUT /product/payment-outs/{payment_out} permission: payment_out-update

Update an existing payment out.

Request Body

application/json
{
  "party_id": 22,
  "payment_type": "cheque",
  "receipt_number": "RCPT-OUT-2026-0018",
  "payment_date": "2026-07-18",
  "description": "Payment for BILL-2026-0011",
  "paid_amount": 78000,
  "allocations": [
    {
      "invoiceable_type": "App\Modules\Product\Models\PurchaseInvoice",
      "invoiceable_id": 91,
      "amount": 78000,
      "notes": "Full settlement"
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Payment Out updated successfully",
  "data": {
    "id": 18,
    "party_id": 22,
    "payment_type": "cheque",
    "receipt_number": "RCPT-OUT-2026-0018",
    "payment_date": "2026-07-18",
    "description": "Payment for BILL-2026-0011",
    "paid_amount": 78000,
    "status": "used",
    "attachments": [],
    "party": {
      "id": 22,
      "display_name": "Ethio Cement Suppliers PLC"
    }
  }
}
DELETE /product/payment-outs/{payment_out} permission: payment_out-delete

Soft-delete a payment out (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Payment Out deleted successfully",
  "data": null
}

Transactions Ledger

GET /product/transactions permission: report-read

Unified ledger — every money movement (sales, purchases, payments, adjustments) in one paginated feed.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "transactions": [
      {
        "id": 512,
        "transaction_no": "TXN-2026-000452",
        "transaction_date": "2026-07-18 10:30:00",
        "amount": 9025,
        "type": "sale",
        "direction": "in",
        "description": "Payment received for INV-2026-0042",
        "reference_number": "RCPT-2026-0033",
        "status": "completed",
        "party_balance_before": 21525.75,
        "party_balance_after": 12500.75,
        "created_at": "2026-07-18 10:30:00",
        "party": {
          "id": 15,
          "display_name": "Blue Nile Trading"
        }
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 512,
    "last_page": 35
  }
}

Expenses & Other Income

Business expenses (with categories/items) and one-off other-income/expense entries outside the sales/purchase cycle.

Expenses /product/expenses

GET /product/expenses permission: expense-read

List expenses. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Expenses retrieved successfully",
  "data": {
    "expenses": [
      {
        "id": 27,
        "expense_category_id": 4,
        "expense_number": "EXP-2026-0019",
        "payment_type": "cash",
        "description": "Office electricity bill",
        "total": 3200,
        "paid_amount": 3200,
        "subtotal": 3200,
        "vat_amount": 0,
        "created_at": "2026-07-18 10:30:00",
        "category": {
          "id": 4,
          "name": "Utilities"
        },
        "items": [
          {
            "id": 1,
            "expense_item_id": 8,
            "quantity": 1,
            "price_per_unit": 3200,
            "amount": 3200,
            "item": {
              "id": 8,
              "item": "Electricity"
            }
          }
        ]
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/expenses permission: expense-create

Create a new expense.

Request Body

application/json
{
  "expense_category_id": 4,
  "expense_number": "EXP-2026-0019",
  "payment_type": "cash",
  "description": "Office electricity bill",
  "total": 3200,
  "paid_amount": 3200,
  "subtotal": 3200,
  "items": [
    {
      "expense_item_id": 8,
      "quantity": 1,
      "price_per_unit": 3200,
      "amount": 3200
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Expense created successfully",
  "data": {
    "id": 27,
    "expense_category_id": 4,
    "expense_number": "EXP-2026-0019",
    "payment_type": "cash",
    "description": "Office electricity bill",
    "total": 3200,
    "paid_amount": 3200,
    "subtotal": 3200,
    "vat_amount": 0,
    "created_at": "2026-07-18 10:30:00",
    "category": {
      "id": 4,
      "name": "Utilities"
    },
    "items": [
      {
        "id": 1,
        "expense_item_id": 8,
        "quantity": 1,
        "price_per_unit": 3200,
        "amount": 3200,
        "item": {
          "id": 8,
          "item": "Electricity"
        }
      }
    ]
  }
}
GET /product/expenses/{expense} permission: expense-read

Get a single expense by id.

Response 200

200 OK
{
  "success": true,
  "message": "Expense retrieved successfully",
  "data": {
    "id": 27,
    "expense_category_id": 4,
    "expense_number": "EXP-2026-0019",
    "payment_type": "cash",
    "description": "Office electricity bill",
    "total": 3200,
    "paid_amount": 3200,
    "subtotal": 3200,
    "vat_amount": 0,
    "created_at": "2026-07-18 10:30:00",
    "category": {
      "id": 4,
      "name": "Utilities"
    },
    "items": [
      {
        "id": 1,
        "expense_item_id": 8,
        "quantity": 1,
        "price_per_unit": 3200,
        "amount": 3200,
        "item": {
          "id": 8,
          "item": "Electricity"
        }
      }
    ]
  }
}
PUT /product/expenses/{expense} permission: expense-update

Update an existing expense.

Request Body

application/json
{
  "expense_category_id": 4,
  "expense_number": "EXP-2026-0019",
  "payment_type": "cash",
  "description": "Office electricity bill",
  "total": 3200,
  "paid_amount": 3200,
  "subtotal": 3200,
  "items": [
    {
      "expense_item_id": 8,
      "quantity": 1,
      "price_per_unit": 3200,
      "amount": 3200
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Expense updated successfully",
  "data": {
    "id": 27,
    "expense_category_id": 4,
    "expense_number": "EXP-2026-0019",
    "payment_type": "cash",
    "description": "Office electricity bill",
    "total": 3200,
    "paid_amount": 3200,
    "subtotal": 3200,
    "vat_amount": 0,
    "created_at": "2026-07-18 10:30:00",
    "category": {
      "id": 4,
      "name": "Utilities"
    },
    "items": [
      {
        "id": 1,
        "expense_item_id": 8,
        "quantity": 1,
        "price_per_unit": 3200,
        "amount": 3200,
        "item": {
          "id": 8,
          "item": "Electricity"
        }
      }
    ]
  }
}
DELETE /product/expenses/{expense} permission: expense-delete

Soft-delete a expense (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Expense deleted successfully",
  "data": null
}
POST /product/expenses/{expense}/items permission: expense-create

Create a new expense.

Request Body

application/json
{
  "expense_category_id": 4,
  "expense_number": "EXP-2026-0019",
  "payment_type": "cash",
  "description": "Office electricity bill",
  "total": 3200,
  "paid_amount": 3200,
  "subtotal": 3200,
  "items": [
    {
      "expense_item_id": 8,
      "quantity": 1,
      "price_per_unit": 3200,
      "amount": 3200
    }
  ]
}

Response 201

201 OK
{
  "success": true,
  "message": "Expense created successfully",
  "data": {
    "id": 27,
    "expense_category_id": 4,
    "expense_number": "EXP-2026-0019",
    "payment_type": "cash",
    "description": "Office electricity bill",
    "total": 3200,
    "paid_amount": 3200,
    "subtotal": 3200,
    "vat_amount": 0,
    "created_at": "2026-07-18 10:30:00",
    "category": {
      "id": 4,
      "name": "Utilities"
    },
    "items": [
      {
        "id": 1,
        "expense_item_id": 8,
        "quantity": 1,
        "price_per_unit": 3200,
        "amount": 3200,
        "item": {
          "id": 8,
          "item": "Electricity"
        }
      }
    ]
  }
}
POST /product/expenses/payments permission: expense_payment-create

Record a standalone expense payment (not tied to one specific expense).

Request Body

application/json
{
  "expense_id": 27,
  "amount": 3200,
  "payment_mode": "cash"
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment recorded successfully",
  "data": null
}
GET /product/expenses/payments/{expensePayment} permission: expense_payment-read

Get a single expense payment by id.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 1,
    "expense_id": 27,
    "amount": 3200
  }
}
GET /product/expenses/payments/{payment} permission: expense_payment-read

Get a single expense payment by id.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "id": 1,
    "expense_id": 27,
    "amount": 3200
  }
}
POST /product/expenses/payments/{payment}/allocate permission: expense_payment-update

Allocate this payment across one or more expenses.

Request Body

application/json
{
  "allocations": [
    {
      "expense_id": 27,
      "amount": 3200
    }
  ]
}

Response 200

200 OK
{
  "success": true,
  "message": "Allocated successfully",
  "data": null
}
GET /product/expenses/{expense}/attachments permission: expense-read

File attachments on this expense.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "attachments": []
  }
}
POST /product/expenses/{expense}/attachments permission: expense-update

Upload a file attachment to this expense (multipart/form-data).

Response 201

201 OK
{
  "success": true,
  "message": "Attachment uploaded successfully",
  "data": null
}
POST /product/expenses/{expense}/items permission: expense_expense_item-create

Add a line item to this expense.

Request Body

application/json
{
  "expense_item_id": 8,
  "quantity": 1,
  "price_per_unit": 3200
}

Response 201

201 OK
{
  "success": true,
  "message": "Line added successfully",
  "data": null
}
PUT /product/expenses/{expense}/items/{item} permission: expense_expense_item-update

Update a line item on this expense.

Request Body

application/json
{
  "quantity": 2
}

Response 200

200 OK
{
  "success": true,
  "message": "Line updated successfully",
  "data": null
}
DELETE /product/expenses/{expense}/items/{item} permission: expense_expense_item-delete

Remove a line item from this expense.

Response 200

200 OK
{
  "success": true,
  "message": "Line removed successfully",
  "data": null
}
POST /product/expenses/{expense}/payments permission: expense_payment-create

Record a payment against this specific expense.

Request Body

application/json
{
  "amount": 3200,
  "payment_mode": "cash"
}

Response 201

201 OK
{
  "success": true,
  "message": "Payment recorded successfully",
  "data": null
}
GET /product/expenses/{expense}/payments permission: expense_payment-read

Payments recorded against this expense.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "payments": []
  }
}

Expense Categories & Items

GET /product/expense-categories permission: expense_category-read

List expense categorys. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Expense Categorys retrieved successfully",
  "data": {
    "categories": [
      {
        "id": 4,
        "name": "Utilities",
        "description": "Electricity, water, internet",
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/expense-categories permission: expense_category-create

Create a new expense category.

Request Body

application/json
{
  "name": "Utilities",
  "description": "Electricity, water, internet"
}

Response 201

201 OK
{
  "success": true,
  "message": "Expense Category created successfully",
  "data": {
    "id": 4,
    "name": "Utilities",
    "description": "Electricity, water, internet",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/expense-categories/{expense_category} permission: expense_category-update

Update an existing expense category.

Request Body

application/json
{
  "name": "Utilities",
  "description": "Electricity, water, internet"
}

Response 200

200 OK
{
  "success": true,
  "message": "Expense Category updated successfully",
  "data": {
    "id": 4,
    "name": "Utilities",
    "description": "Electricity, water, internet",
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/expense-categories/{expense_category} permission: expense_category-delete

Soft-delete a expense category (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Expense Category deleted successfully",
  "data": null
}
GET /product/expense-items permission: expense_item-read

List expense items. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Expense Items retrieved successfully",
  "data": {
    "items": [
      {
        "id": 8,
        "item": "Electricity",
        "price_per_unit": 3200,
        "created_at": "2026-07-18 10:30:00",
        "updated_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/expense-items permission: expense_item-create

Create a new expense item.

Request Body

application/json
{
  "item": "Electricity",
  "price_per_unit": 3200
}

Response 201

201 OK
{
  "success": true,
  "message": "Expense Item created successfully",
  "data": {
    "id": 8,
    "item": "Electricity",
    "price_per_unit": 3200,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
GET /product/expense-items/{expense_item} permission: expense_item-read

Get a single expense item by id.

Response 200

200 OK
{
  "success": true,
  "message": "Expense Item retrieved successfully",
  "data": {
    "id": 8,
    "item": "Electricity",
    "price_per_unit": 3200,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
PUT /product/expense-items/{expense_item} permission: expense_item-update

Update an existing expense item.

Request Body

application/json
{
  "item": "Electricity",
  "price_per_unit": 3200
}

Response 200

200 OK
{
  "success": true,
  "message": "Expense Item updated successfully",
  "data": {
    "id": 8,
    "item": "Electricity",
    "price_per_unit": 3200,
    "created_at": "2026-07-18 10:30:00",
    "updated_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/expense-items/{expense_item} permission: expense_item-delete

Soft-delete a expense item (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Expense Item deleted successfully",
  "data": null
}

Other Income / Expense /product/incomes-expenses

GET /product/incomes-expenses permission: other_income_expense-read

List other income/expenses. Paginated; supports search, page, per_page query params.

Response 200

200 OK
{
  "success": true,
  "message": "Other Income/Expenses retrieved successfully",
  "data": {
    "entries": [
      {
        "id": 12,
        "type": "income",
        "name": "Bank Interest Income",
        "description": "Q2 interest on savings account",
        "amount": 640,
        "formatted_amount": "640.00",
        "date": "2026-07-18",
        "created_at": "2026-07-18 10:30:00"
      }
    ]
  },
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}
POST /product/incomes-expenses permission: other_income_expense-create

Create a new other income/expense.

Request Body

application/json
{
  "type": "income",
  "name": "Bank Interest Income",
  "description": "Q2 interest on savings account",
  "amount": 640,
  "date": "2026-07-18"
}

Response 201

201 OK
{
  "success": true,
  "message": "Other Income/Expense created successfully",
  "data": {
    "id": 12,
    "type": "income",
    "name": "Bank Interest Income",
    "description": "Q2 interest on savings account",
    "amount": 640,
    "formatted_amount": "640.00",
    "date": "2026-07-18",
    "created_at": "2026-07-18 10:30:00"
  }
}
GET /product/incomes-expenses/{otherIncomeExpense} permission: other_income_expense-read

Get a single other income/expense by id.

Response 200

200 OK
{
  "success": true,
  "message": "Other Income/Expense retrieved successfully",
  "data": {
    "id": 12,
    "type": "income",
    "name": "Bank Interest Income",
    "description": "Q2 interest on savings account",
    "amount": 640,
    "formatted_amount": "640.00",
    "date": "2026-07-18",
    "created_at": "2026-07-18 10:30:00"
  }
}
PUT /product/incomes-expenses/{otherIncomeExpense} permission: other_income_expense-update

Update an existing other income/expense.

Request Body

application/json
{
  "type": "income",
  "name": "Bank Interest Income",
  "description": "Q2 interest on savings account",
  "amount": 640,
  "date": "2026-07-18"
}

Response 200

200 OK
{
  "success": true,
  "message": "Other Income/Expense updated successfully",
  "data": {
    "id": 12,
    "type": "income",
    "name": "Bank Interest Income",
    "description": "Q2 interest on savings account",
    "amount": 640,
    "formatted_amount": "640.00",
    "date": "2026-07-18",
    "created_at": "2026-07-18 10:30:00"
  }
}
DELETE /product/incomes-expenses/{otherIncomeExpense} permission: other_income_expense-delete

Soft-delete a other income/expense (recoverable via restore).

Response 200

200 OK
{
  "success": true,
  "message": "Other Income/Expense deleted successfully",
  "data": null
}
GET /product/incomes-expenses/summary permission: other_income_expense-read

Totals of other income vs. other expense for the period.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "total_income": 12400,
    "total_expense": 3100
  }
}

Cash, Bank & Cheques

Also Common/shared — full reference on the Home page → Cash, Bank & Cheques (prefix /banking, 48 endpoints: bank accounts, bank/cash adjustments, bank transfers, cheques).

Reports

36 read-only reporting endpoints, all under /product/reports, gated by report-read.

GET /product/reports/bank/statement permission: report-read

Bank account statement — every transaction posted to a bank account over a date range. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/bank/summary permission: report-read

Bank balances summary across all bank accounts. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/cash-flow permission: report-read

Cash-in vs cash-out over a date range. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/day-book permission: report-read

Every transaction recorded on a given day, across all types. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/expense-aging permission: report-read

Unpaid expenses grouped by how overdue they are. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/expense-ledger permission: report-read

Full expense ledger, chronological. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/expenses permission: report-read

Expense totals grouped by category over a date range. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/expenses-by-category permission: report-read

Expense totals broken down by category. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/item-report-by-party permission: report-read

Which items a specific party has bought/sold. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/item-wise-discount permission: report-read

Discount given, broken down by item. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/item-wise-profit-loss permission: report-read

Profit/loss per item (selling price vs. cost). Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/items-by-party permission: report-read

Items associated with a given party's transaction history. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/low-stock-summary permission: report-read

Items at or below their reorder level. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/low-stock-summary-optimized permission: report-read

Performance-optimized variant of the low-stock summary for large catalogs. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/order-item-transaction permission: report-read

Item-level detail for sales/purchase orders. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/order-transaction permission: report-read

Order-level transaction summary. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/parties-with-financials permission: report-read

Every party with their current balance and credit status. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/party-sales-report permission: report-read

Read-only report endpoint. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/purchase-aging permission: report-read

Unpaid purchase bills grouped by how overdue they are. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/purchase-by-item permission: report-read

Purchase totals grouped by item. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/purchase-ledger permission: report-read

Full purchase ledger, chronological. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/sale-purchase-by-category permission: report-read

Sales vs. purchases, grouped by item category. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/sales-aging permission: report-read

Unpaid sales invoices grouped by how overdue they are. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/sales-purchase-by-category permission: report-read

Sales vs. purchases, grouped by item category. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/sales-purchase-discount permission: report-read

Discount given across sales and purchases. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/stock-detail permission: report-read

Detailed per-batch stock listing. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/stock-details permission: report-read

Detailed per-batch stock listing. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/stock-summary permission: report-read

Stock summary across all items and warehouses. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/stock-summary-by-category permission: report-read

Stock summary grouped by item category. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/tax-rate-report permission: report-read

Transactions grouped by applied tax rate. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/tax-report permission: report-read

Tax collected/paid summary for the period. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/top-parties-by-sales permission: report-read

Highest-value customers by total sales. Accepts optional filter query params (warehouse_id, item_category_id, search, date range).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // report-specific columns
    ],
    "summary": {
      "total": 184500
    }
  }
}
GET /product/reports/item-details/{item} permission: report-read

Full detail report for a single item — stock, pricing, and transaction history.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "item": {
      "id": 101,
      "uuid": "b6e2c1a4-...",
      "item_type": "goods",
      "name": "Cement 50kg",
      "sku_code": "CEM-50KG-001",
      "barcode": "8901030875021",
      "description": "OPC 42.5 grade cement bag",
      "image": "https://.../item_images/cem.jpg",
      "hsn_code": "2523",
      "tin_applicable": true,
      "selling_price": 950,
      "purchase_price": 800,
      "mrp_price": 1000,
      "discount_type": "percentage",
      "discount_value": 5,
      "is_active": true,
      "notes": "Bulk discount available",
      "total_stock": 500,
      "available_stock": 480,
      "created_at": "2026-07-18 10:30:00",
      "updated_at": "2026-07-18 10:30:00",
      "category": {
        "id": 3,
        "name": "Building Materials",
        "code": "BLD"
      }
    },
    "movements": []
  }
}
GET /product/reports/party-financials/{party} permission: report-read

Full financial report for a single party — balance, aging, transaction history.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "email": "info@bluenile.com",
      "phone": "0911909090",
      "tin_number": "0012345678",
      "vat_no": "VAT-00981",
      "tax_reg_type": "vat_registered",
      "currency": "Birr",
      "country_default": "Ethiopia",
      "balance": 12500.75,
      "opening_balance_amount": 5000,
      "opening_balance_type": "debit",
      "opening_date": "Jan 1, 2026",
      "is_active": true,
      "billing_address": "Bole, Addis Ababa",
      "created_at": "Jul 18, 2026"
    },
    "aging": {}
  }
}
GET /product/reports/party-sales-detail/{party} permission: report-read

Detailed sales history for a single party.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "party": {
      "id": 15,
      "uuid": "9f2c1a3e-...",
      "party_type": "customer",
      "legal_name": "Blue Nile Trading PLC",
      "display_name": "Blue Nile Trading",
      "email": "info@bluenile.com",
      "phone": "0911909090",
      "tin_number": "0012345678",
      "vat_no": "VAT-00981",
      "tax_reg_type": "vat_registered",
      "currency": "Birr",
      "country_default": "Ethiopia",
      "balance": 12500.75,
      "opening_balance_amount": 5000,
      "opening_balance_type": "debit",
      "opening_date": "Jan 1, 2026",
      "is_active": true,
      "billing_address": "Bole, Addis Ababa",
      "created_at": "Jul 18, 2026"
    },
    "sales": []
  }
}
GET /product/reports/party-sales-report permission: report-read

Sales totals grouped by party.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "rows": [
      // per-party sales totals
    ]
  }
}
GET /product/reports/stock/transfers permission: report-read

Report of all stock transfers between warehouses over a date range.

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "transfers": []
  }
}

Bulk Import

Spreadsheet-based bulk import for onboarding existing data. Each accepts a multipart/form-data file upload.

Not documented in this pass.

Settings

Per-item settings plus the tenant-wide general/transaction settings (Common/shared, prefix /settings — see Home page).

GET /product/items/{item}/settings permission: item_setting-read

Get per-item settings (e.g. tax applicability, stock-maintenance flags).

Response 200

200 OK
{
  "success": true,
  "message": "Retrieved successfully",
  "data": {
    "item_id": 101,
    "tin_applicable": true,
    "enable_stock_maintenance": true
  }
}
POST /product/items/{item}/settings permission: item_setting-create

Create per-item settings.

Request Body

application/json
{
  "tin_applicable": true,
  "enable_stock_maintenance": true
}

Response 201

201 OK
{
  "success": true,
  "message": "Settings created successfully",
  "data": null
}
PUT /product/items/{item}/settings permission: item_setting-update

Update per-item settings.

Request Body

application/json
{
  "tin_applicable": false
}

Response 200

200 OK
{
  "success": true,
  "message": "Settings updated successfully",
  "data": null
}
DELETE /product/items/{item}/settings permission: item_setting-delete

Soft-delete per-item settings, reverting to tenant defaults.

Response 200

200 OK
{
  "success": true,
  "message": "Settings deleted successfully",
  "data": null
}
Service Module

Documentation coming soon

The Service module API (catalog, job orders, service invoices & purchases) is fully built and live — this reference page is being written next, once the Product docs are in developers' hands. This nav item stays active so the link is ready as soon as it ships.

Manufacturing Module

Documentation coming soon

The Manufacturing module API (raw materials, BOM, production & sales orders, pricing) is fully built and live — this reference page is being written next. This nav item stays active so the link is ready as soon as it ships.