Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.wellapp.ai/llms.txt

Use this file to discover all available pages before exploring further.

InvoiceItem represents a single line item on an invoice — a discrete charge for a product or service with its own quantity, price, tax, and period metadata. It belongs exclusively to one Invoice via a mandatory many-to-one relation. Key associations are: Invoice (parent document), LedgerAccount (accounting chart of accounts classification), TaxRate (the well-catalog tax rate that was applied), and Media (an optional supporting document such as an image or receipt). The accounting_classification JSONB field carries the AI-produced Well-taxonomy posting intent used by the journal-entry builder to classify the line for double-entry accounting.
NamingValue
ObjectInvoice Item
Resource type (JSON:API type)invoice_item
Collection / records rootinvoice_items
REST base/v1/invoice-items
Entity classInvoiceItem

API operations

OperationMethod & pathStatus
ListGET /v1/invoice-items✅ Implemented
RetrieveGET /v1/invoice-items/{id}✅ Implemented
CreatePOST /v1/invoice-items🟡 Planned
UpdatePATCH /v1/invoice-items/{id}🟡 Planned
DeleteDELETE /v1/invoice-items/{id}🟡 Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
invoice_item_idstring, UUID✅ Yesunique; generated by gen_random_uuid() at creationPublic stable identifier for this invoice line item. Used in all API surfaces and references.
line_idstring✅ Yesmax length 50; table-wide composite-unique with invoice (no two lines — active or deleted — on the same invoice share a line_id)Provider-assigned or pipeline-generated identifier for this line within the parent invoice. Used to deduplicate re-ingested lines. The unique constraint is not partial: a soft-deleted line_id blocks reuse on the same invoice until the row is hard-deleted.
skustring⚪ Nomax length 100Stock-keeping unit or product code from the originating system (ERP, e-commerce, MCP connector).
namestring✅ Yesmax length 255Human-readable label for the line item. Mapped directly from the originating document’s line description.
descriptionstring⚪ Nomax length 1000Extended free-text description of the line item, supplying additional context beyond the name.
unit_pricedecimal(15,2)✅ YesCHECK unit_price >= 0Price per unit in the invoice’s currency, before any discount or tax. Always non-negative.
currencystring, enum (CurrencyCodeEnum)✅ YesPostgreSQL native enum currency_code_enumISO 4217 three-letter codes, e.g. USD, EUR, GBP, JPY, CHF, CAD, AUD, …Currency of unit_price, line_total, tax_amount, discount, and quantity-derived amounts on this line.
unitstring, enum (InvoiceLineUnitEnum)⚪ NoPostgreSQL native enum invoice_line_unit_enum; nullableEA, PC, SET, PR, DZ, C62, MIL, KT, PK, BX, HUR, MIN, SEC, DAY, WEE, MON, ANN, QT, KGM, GRM, TNE, LBR, ONZ, CWT, STN, LTN, MTR, CMT, MMT, KMT, INH, FOT, YRD, SMI, MTK, CMK, INK, FTK, YDK, ACR, HAR, LTR, MLT, MTQ, CMQ, INQ, FTQ, YDQ, GAL, PT, BYT, KBY, MBY, GBY, TBY, BIT, KBI, MBI, GBI, KWH, MWH, WHR, KWT, MWT, BTU, CAL, CEL, FAH, KEL, BAR, PSI, PAL, SRV, LIC, USR, SES, TXN, REQ, PAG, VIS, CLI, IMP, PTC, BPS, SHR, LOT, PNT, TOL, MOL, PPM, PPB, PH, UNT, OTH, NAUN/CEFACT unit-of-measure code for the quantity on this line. Covers physical quantities, time, data, and service units.
quantitydecimal(15,2)⚪ NoCHECK quantity >= 0; nullableNumber of units. Stored as decimal(15,2) to accommodate fractional service quantities. Populated by the pipeline ingestion from connector data.
min_quantitydecimal(15,2)⚪ NoCHECK min_quantity >= 0; nullableMinimum purchasable quantity for this line, used when the invoice carries tiered or min/max quantity brackets.
max_quantitydecimal(15,2)⚪ NoCHECK max_quantity >= min_quantity; nullableMaximum purchasable quantity for this line. Must be >= min_quantity when both are present.
line_totaldecimal(15,2)⚪ NoCHECK line_total >= 0; nullableTotal amount for this line (unit_price × quantity − discount), excl. tax. Populated by the pipeline; may differ from a client-computed product when rounding rules apply.
discountdecimal(15,2)⚪ NoCHECK discount >= 0; nullable; default 0Discount amount applied to this line, expressed as an absolute monetary value in the line’s currency. Always non-negative.
tax_ratedecimal(5,2)⚪ NoCHECK tax_rate >= 0 AND tax_rate <= 100; nullable0.00 – 100.00Percentage rate of tax applied to this line, e.g. 20.00 for 20% VAT. Distinct from the applied_tax_rate relationship which links to the well-catalog tax rate entry.
tax_categorystring, enum (TaxCategoryEnum)⚪ NoPostgreSQL native enum tax_category_enum; nullablestandard, reduced, super_reduced, zero_rated, exempt, reverse_charge, out_of_scope, government, municipal, regulatory, statutory, administrative, medical_exempt, medical_reduced, medical_standard, pharmaceutical, hospital, dental, veterinary, education_exempt, education_reduced, books, cultural, research, library, food_basic, food_standard, food_luxury, beverages_non_alcoholic, beverages_alcoholic, restaurant, catering, property_residential, property_commercial, construction_new, construction_renovation, land, property_management, transport_public, transport_passenger, transport_freight, vehicle_sales, vehicle_parts, fuel, parking, utilities_domestic, utilities_commercial, water, sewage, waste_management, telecommunications, energy_renewable, financial_exempt, insurance_exempt, investment, banking, credit, foreign_exchange, software_license, software_saas, digital_services, cloud_computing, data_processing, telecommunications_digital, electronic_delivery, manufacturing, industrial_equipment, raw_materials, chemicals, mining, agriculture, forestry, entertainment, sports, gambling, tourism, hospitality, recreation, export, import, intrastat, customs, free_trade_zone, diplomatic, legal_services, accounting, consulting, professional, notary, mixed_rate, threshold_based, seasonal, promotional, margin_scheme, reverse_auction, unknown, pending, other, not_applicableSemantic classification of the tax treatment on this line, aligned to EU/global tax-code taxonomy.
tax_schemestring, enum (TaxSchemeEnum)⚪ NoPostgreSQL native enum tax_scheme_enum; nullableVAT, EU_VAT, UK_VAT, MOSS_VAT, OSS_VAT, IOSS_VAT, GST, AU_GST, CA_GST, CA_HST, CA_PST, CA_QST, IN_GST, IN_CGST, IN_SGST, IN_IGST, IN_UTGST, SG_GST, MY_GST, MY_SST, NZ_GST, SALES_TAX, US_STATE_TAX, US_LOCAL_TAX, US_USE_TAX, CA_RETAIL_TAX, NY_SALES_TAX, TX_SALES_TAX, JCT, KR_VAT, CN_VAT, RU_VAT, BR_ICMS, BR_IPI, BR_PIS_COFINS, MX_IVA, AR_IVA, CL_IVA, EXCISE_TAX, LUXURY_TAX, SIN_TAX, CARBON_TAX, FUEL_TAX, TOBACCO_TAX, ALCOHOL_TAX, DIGITAL_TAX, CORPORATE_TAX, WITHHOLDING_TAX, BRANCH_PROFITS_TAX, TURNOVER_TAX, GROSS_RECEIPTS_TAX, CUSTOMS_DUTY, IMPORT_DUTY, EXPORT_DUTY, ANTI_DUMPING_DUTY, COUNTERVAILING_DUTY, TARIFF, PROPERTY_TAX, TRANSFER_TAX, STAMP_DUTY, INHERITANCE_TAX, GIFT_TAX, WEALTH_TAX, PAYROLL_TAX, SOCIAL_SECURITY_TAX, UNEMPLOYMENT_TAX, DISABILITY_TAX, MEDICARE_TAX, MUNICIPAL_TAX, CITY_TAX, COUNTY_TAX, DISTRICT_TAX, TOURIST_TAX, OCCUPANCY_TAX, FINANCIAL_TRANSACTION_TAX, BANK_TAX, INSURANCE_PREMIUM_TAX, TELECOM_TAX, UTILITY_TAX, AVIATION_TAX, SHIPPING_TAX, ENVIRONMENTAL_TAX, PLASTIC_TAX, PACKAGING_TAX, WASTE_TAX, CONGESTION_TAX, OTHER, MIXED, UNKNOWN, NONE, PENDINGThe tax framework under which the line is taxed, e.g. EU_VAT, GST, US_STATE_TAX. Covers 90+ global tax schemes.
tax_amountdecimal(15,2)⚪ NoCHECK tax_amount >= 0; nullableAbsolute tax amount for this line in the line’s currency, derived from unit_price × quantity × tax_rate / 100.
accounting_unit_pricedecimal(15,2)⚪ NonullableUnit price converted to the workspace’s functional accounting currency. Set by the FX-matching pipeline when the invoice currency differs from the workspace currency.
accounting_line_totaldecimal(15,2)⚪ NonullableLine total converted to the workspace’s functional accounting currency. Parallel to accounting_unit_price for full multi-currency double-entry support.
period_starttimestamp⚪ NonullableStart of the service period covered by this line item. Used for subscription, retainer, and recurring-service invoices.
period_endtimestamp⚪ NonullableEnd of the service period covered by this line item. Paired with period_start for accrual accounting period allocation.
accounting_classificationjsonb⚪ Nonullable; partial functional index on (accounting_classification->>‘status’) WHERE deleted_at IS NULL (idx_invoice_items_accounting_classification_status — defined in Migration20260525101000 only, not expressible via MikroORM decorators){ status: ‘ready’, wellCoaVersion: string, intent: WellPostingIntent, rawFacts: InvoiceItemAccountingLLMFacts } | { status: ‘needs_review’, wellCoaVersion: string, reason: InvoiceItemAccountingReviewReason, rawFacts?: …, details?: string }AI-produced Well-taxonomy accounting classification for this line. Written by the journal-entry draft builder. status=‘ready’ means a valid WellPostingIntent was resolved and the line can be posted to the ledger. status=‘needs_review’ carries a reason code (missing_accounting_facts, llm_requested_review, unknown_semantic_role, unsupported_invoice_item_posting_kind, unsupported_invoice_item_transfer_role, invalid_accounting_qualifier, posting_intent_halt) and blocks automatic posting.
created_attimestamp, 🔒 system✅ Yesset once on insert via onCreate lifecycle hookISO 8601 timestamp recording when the invoice item row was first persisted.
updated_attimestamp, 🔒 system⚪ Noset on insert and refreshed on every update via onCreate/onUpdate lifecycle hooksISO 8601 timestamp of the most recent write to this row.
deleted_attimestamp⚪ Nonullable; soft-delete sentinel; partial indexes exclude rows where deleted_at IS NOT NULLWhen set, marks this line as logically deleted. All active-record queries filter deleted_at IS NULL. Cleared only by an explicit restore operation.

Relationships

NameTypeRequiredDescription
invoiceto-one (invoice)✅ YesThe parent invoice to which this line belongs. Non-nullable ManyToOne to the Invoice entity. A line cannot exist without its parent. Indexed via idx_invoice_items_invoice_deleted (invoice + deleted_at) and the partial index idx_invoice_items_invoice_active (invoice_pk WHERE deleted_at IS NULL) for hot UPDATE paths during invoice-merge operations.
ledger_accountto-one (ledger_account)⚪ NoOptional link to the LedgerAccount (chart-of-accounts entry) that this line item has been assigned to. Set by the accounting classification pipeline or by a user override. Indexed via idx_invoice_items_ledger_account.
applied_tax_rateto-one (tax_rate)⚪ NoOptional link to the Well-catalog TaxRate entry that was applied when computing the tax on this line. Distinct from the scalar tax_rate column which stores the raw percentage. Indexed via idx_invoice_items_applied_tax_rate.
mediato-one (media)⚪ NoOptional supporting document or image attached to this line (e.g. a product image, receipt scan, or delivery note). ManyToOne to the Media entity. Indexed via idx_invoice_items_media.

System-computed

  • invoice_item_id is generated by PostgreSQL gen_random_uuid() at insert time and is immutable thereafter.
  • created_at is set once by the MikroORM onCreate lifecycle hook; it is never updated.
  • updated_at is set by both onCreate and onUpdate hooks — it reflects the wall-clock time of the most recent write.
  • deleted_at is null on creation. Setting it to a non-null timestamp constitutes a soft delete. The partial indexes idx_invoice_items_invoice_active and idx_invoice_items_accounting_classification_status both carry WHERE deleted_at IS NULL to exclude deleted rows from hot read paths.
  • The composite unique constraint (invoice, line_id) is table-wide (not partial): it enforces that no two lines — active or soft-deleted — on the same invoice share the same line_id. A soft-deleted line_id cannot be reused for a new line on the same invoice without first hard-deleting the old row.
  • accounting_unit_price and accounting_line_total are derived by the FX-matching pipeline (invoice.service.ts + fx-rate.service.ts) when the line currency differs from the workspace’s functional currency. They are never computed by the API layer on a write request.
  • accounting_classification is written exclusively by the invoice-journal-entry-draft builder (services/accounting/invoice-journal-entry-draft.builder.ts) and classifyInvoiceItemAccounting(). It is never set by a direct API mutation. Its status field is indexed via a JSONB functional partial index (migration-only; MikroORM decorators cannot express JSONB key expressions or partial predicates, so schema:fresh diverges from production on this index).
  • discount defaults to 0 at the database level when not supplied by the connector.

Example

{
  "data": {
    "type": "invoice_item",
    "id": "a3f7b2c1-84d9-4e10-b6e0-2f1d5c839740",
    "attributes": {
      "invoice_item_id": "a3f7b2c1-84d9-4e10-b6e0-2f1d5c839740",
      "line_id": "line-001",
      "sku": "SVC-CONSULTING-2026",
      "name": "Strategic consulting — Q2 2026",
      "description": "Monthly advisory retainer covering financial modelling and board prep.",
      "unit_price": "4500.00",
      "currency": "EUR",
      "unit": "MON",
      "quantity": "1.00",
      "min_quantity": null,
      "max_quantity": null,
      "line_total": "4500.00",
      "discount": "0.00",
      "tax_rate": "20.00",
      "tax_category": "standard",
      "tax_scheme": "EU_VAT",
      "tax_amount": "900.00",
      "accounting_unit_price": "4500.00",
      "accounting_line_total": "4500.00",
      "period_start": "2026-04-01T00:00:00.000Z",
      "period_end": "2026-06-30T23:59:59.000Z",
      "accounting_classification": {
        "status": "ready",
        "wellCoaVersion": "1.0.0",
        "intent": {
          "semanticRole": "operating_expense",
          "postingKind": "invoice_accrual",
          "documentPolarity": "purchase",
          "wellCoaVersion": "1.0.0"
        },
        "rawFacts": {
          "well_semantic_role": "operating_expense",
          "posting_kind": "invoice_accrual",
          "document_polarity": "purchase",
          "tax_behavior": "exclusive",
          "confidence": 0.94,
          "classifier_model": "claude-opus-4-6"
        }
      },
      "created_at": "2026-04-30T09:14:22.000Z",
      "updated_at": "2026-05-02T11:07:55.000Z",
      "deleted_at": null
    },
    "relationships": {
      "invoice": {
        "data": { "type": "invoice", "id": "d8e1f4a2-3c7b-4b85-9e2d-6a0f7c1e4821" }
      },
      "ledger_account": {
        "data": { "type": "ledger_account", "id": "f1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" }
      },
      "applied_tax_rate": {
        "data": { "type": "tax_rate", "id": "b9c8d7e6-f5a4-3b2c-1d0e-9f8e7d6c5b4a" }
      },
      "media": {
        "data": null
      }
    }
  }
}
Source: apps/api/src/database/entities/InvoiceItem.ts · domain: financial-graph · tier: Supporting