Skip to content

Finance (Vendor)

Finance covers transactions, expenses, refunds, invoices, receivables, and reports across multiple subroutes. Module finance applies; some screens are dense—use store context and API filters consistently.

Operational flows that combine POS, orders, and receivables are summarized in Sales, POS & receivables (procedures).


GoalsNon-goals
Operational money movement visibilityReplace certified accounting close
Link vendor UI routes to finance APIsDocument tax jurisdiction rules

  • Hub at /dashboard/finances; ledger and order-settlement variants per table below.
  • Detailed field semantics and filters: Swagger / API conventions.
  • Vendor finance UI must not deep-link the Clover merchant dashboard (https://www.clover.com). Order rows open /dashboard/orders/:id, tax settings open catalog taxes, billing statements open /dashboard/billing, and Clover CDN images/SVGs are served from apps/vendor-web/public/images/finance/. Backend Clover API hosts (api.clover.com) are unchanged.

  1. Open Finances hub → pick tab/filter.
  2. Drill to transaction detail or order settlement per investigation.
  3. Export or reconcile per ops process (if UI provides export).

  • Finance data is highly sensitive—role-gate, audit exports, no shared screenshots.

IDScenarioExpected
F1Employee with financeRoutes open
F2Employee without financeBlocked
F3Transaction detailMatches API source of truth

RoutePurpose
/dashboard/financesFinances hub — account transaction list with tabs (e.g. All / cash-in / collected-by-admin) using transactions overview + list APIs
/dashboard/finance/cash-bookRemoved — SPA redirects to /dashboard/finance/transactions (bookmarks and old links)
/dashboard/finance/transactionsLedger transactions (includes manual create modal for account transactions)
/dashboard/finance/transactions/:idAccount transaction detail (account_transactions row)
/dashboard/finance/transactions/ordersOrder-settlement style transaction list
/dashboard/finance/transactions/orders/:idPer-order settlement detail
/dashboard/finance/expensesExpense lines (P&L-style) — list supports multi-select batch status updates (approve / mark paid / cancel), mirroring receivables batch UX
/dashboard/finance/expenses/:idExpense detail + lifecycle updates + activity timeline
/dashboard/finance/refundsRefund requests / status
/dashboard/finance/invoicesInvoice list with payment progress and overdue tracking
/dashboard/finance/invoices/createCreate invoice (manual draft → API create)
/dashboard/finance/invoices/:idInvoice detail (summary, customer, issue/due timeline)
/dashboard/finance/receivablesCustomer debt / BNPL-style receivable lines (scoped via order → store)
/dashboard/finance/sales-overviewSales overview — period summary (gross/net sales, taxes, tips), tender types, cash deposits by device, revenue classes; default landing when opening Finance in the sidebar
/dashboard/finance/reportFinancial reports — pick the report type from the dropdown (full-width content); child paths like /dashboard/finance/report/by-time stay bookmarkable
/dashboard/finance/report/taxesTaxes — sales tax by rate (applicable sales, collected, refunded, net) for completed paid orders in the selected period

Export Excel builds an .xlsx with a short cover block (report title, reporting period, store scope, optional “prepared by”, generation timestamp, and a footer note about USD-style negatives and reconciling tax/tender against the POS register journal). Data sheets use typed columns (currency, integers, dates where applicable), frozen header row, AutoFilter, and light borders so the file reads like an ops workbook rather than a raw CSV paste.

The Point of Sale → Revenue by product report uses the same export helper and cover pattern for consistency with dashboard finance exports.

Low stock — “All stores” for vendor employees

Section titled “Low stock — “All stores” for vendor employees”

When the store filter is All stores, the finance Low stock KPI strip and table call catalog overview / low-stock without a store_id so the backend can aggregate across every store the employee is authorized to access. (Other catalog screens still auto-scope to the employee’s current store.) Choosing a single store in the filter sends that store_id explicitly.

Channel charts and the daily trend block are collapsed by default so the period summary, export, and daily breakdown table stay closer together; expand when you need the visuals.

Paths under /dashboard/finance and /dashboard/finances map to module finance. See Vendor panel overview.

The client vendor-finance.api.ts documents the split:

  • /vendor/finance/transactionsaccount_transactions ledger rows (balance-style fields such as current_balance, transaction_type, method, ref).
  • /vendor/finance/expenses — analytical expense lines for vendor-scoped P&L (not the same as customer invoice receivables).
  • /vendor/finance/invoices — issued invoice rows (invoices) with status (unpaid, partially_paid, paid, overdue) and aggregate payment progress.
  • Invoice rows are now auto-synced from orders: created on order creation and updated when order payment status changes.
  • /vendor/finance/receivablescustomer debt lines tied to orders (BNPL / installments-style); distinct from generic ledger expenses.

Backend modules and migrations define how these rows relate to AccountTransaction and customer invoices; treat the three surfaces above as different screens in the vendor app.

All prefixed under /vendor/finance/ in apps/vendor-web/src/services/vendor-finance.api.ts:

SurfaceEndpoints
Transactions.../transactions/overview, .../transactions, POST .../transactions (manual ledger record), .../transactions/:id
Order transactions.../transactions/orders/overview, .../transactions/orders, .../transactions/orders/:id
Expenses.../expenses/overview, .../expenses (list supports tab (all | pending | approved | paid | cancelled), status, expense_type, date/search/sort), .../expenses/:id (GET detail), PATCH .../expenses/:id (update expense_status / description; writes audit_logs), PATCH .../expenses/batch (batch: approve | mark_paid | cancel; skips ineligible rows; writes audit_logs per update)
Refunds.../refunds/overview, .../refunds, .../refunds/:refundId (PATCH updates status)
Invoices.../invoices/overview, .../invoices (list; query filters: store_id, status, tab (all | unpaid | partial | paid | overdue), date_from / date_to, user_id, amount_min / amount_max, search, sort), POST .../invoices (create invoice for existing order in authorized store), .../invoices/:id (GET detail)
Receivables.../receivables/overview, .../receivables (list; query filters: store_id, status, tab (all | unpaid | partial | paid | overdue), date_from / date_to, user_id, amount_min / amount_max, search, sort), .../receivables/:id (GET detail with payments + activities), PATCH .../receivables/:id (edit due date / paid amount partial), PATCH .../receivables/batch (batch settle: mark_paid | cancel | waive; writes audit_logs with JWT actor per row)
Reports.../report/summary, .../report/daily, .../report/staff (per linked vendor employee on orders)

Report summary / daily: totals and each daily row include order_discount: coupon + store + line-item discounts on orders that count toward gross_sales / refunds in the same window (and the same optional sale_type filter: retail vs wholesale). The expenses field remains the sum of vendor expense ledger lines in range (not attributed by sale channel). Retail and wholesale finance report screens use order_discount for the “discount” column so it matches OrderSaleType–scoped orders instead of reusing expenses.

When sale_type is set (0 retail or 1 wholesale), net_revenue is (gross_sales − tax_total) − refunds − order_discount so channel revenue is not reduced by store-wide ledger expenses on days with no orders in that channel. Without sale_type, net_revenue is (gross_sales − tax_total) − expenses − refunds (P&L-style).

On the vendor retail / wholesale report charts, Revenue composition uses net_revenue, order_discount, and refunds only (not the expenses ledger), so the donut matches order-scoped statistics shared across channels.

Receivables / “thu nợ”: the vendor app lists receivable lines and KPIs via the endpoints above. Use quick tabs (all, unpaid, partial, paid, overdue) for fast filtering, then open detail (GET /vendor/finance/receivables/:id) to inspect payment/activity history. PATCH /vendor/finance/receivables/:id supports editing payment_deadline and absolute paid_amount (partial payment correction). PATCH /vendor/finance/receivables/batch settles selected rows at full balance with actions mark_paid (optional receivable_payments row when there was a remainder), cancel, or waive; each affected row appends audit_logs including the vendor JWT actor and optional note. Staff may still use order detail flows where applicable—see Sales, POS & receivables (procedures) §5 for how order payment_status relates to receivable rows.

Expenses: expense lines now support POS-vendor categories such as coupon, vendor_payout, delivery_payout, staff_salary, and pos_operational (plus legacy values). Lifecycle status is tracked via expense_status (pending, approved, paid, cancelled) with quick tabs in the list and editable status in detail (PATCH /vendor/finance/expenses/:id). PATCH /vendor/finance/expenses/batch applies approve (pending → approved), mark_paid (pending or approved → paid), or cancel (to cancelled when not already paid/cancelled); each updated row appends audit_logs with optional note and audit_context default finance_expenses.

  • POS order flow now auto-creates delivery_payout expense rows when delivery.partner_delivery_charge is provided.
  • Purchase lot flow now auto-creates vendor_payout expense rows (pending) when a lot is created, so supplier payables are visible in Finance > Expenses.

Refund status changes: PATCH /vendor/finance/refunds/:refundId accepts action (approved | rejected | refunded) and optional admin_note. Optional audit_context (finance_refunds | order_detail) tags the audit row so Orders (Vendor) activity logs show whether the change was made from the refunds list or the order detail screen.

When an order is marked refunded, wallet refunds (method wallet) credit customer wallet balance and write a wallet ledger row. Refund amount is computed on the gross returned lines (line subtotal after item discount plus tax and add-ons). Loyalty is rolled back proportionally to the refunded amount ratio against order_amount, and coupon redemption analytics exclude orders whose payment_status is refunded.

List responses use shared meta pagination (page, limit, total_pages). GET /vendor/finance/transactions and GET /vendor/finance/expenses also include meta.amount_sum: the sum of amount over the rows matching the current filters (same scope as meta.total), as a number for UI totals.

Manual account transaction create (POST /vendor/finance/transactions): vendor users can record analytical ledger rows for authorized stores (e.g. manual cash-in or admin-collected adjustment) with payload keys store_id, transaction_type (CASH_IN | COLLECTED_BY_ADMIN), from_type (store | deliveryman), from_id, amount, plus optional current_balance, method, ref, note, created_at, updated_at (ISO datetime). created_at and updated_at are legacy timestamp overrides for migration/compatibility use-cases. This flow writes account_transactions + audit_logs and intentionally does not mutate wallet balances.

Vendor users can now configure invoice/receipt rendering defaults and SMTP delivery from Settings:

  • /dashboard/settings/receipt-template — receipt template metadata, logo image, sample line items, and receipt options for suggested tips, signature line, and invoice barcode
  • /dashboard/settings/invoice-template — invoice template metadata, logo image, and sample line items
  • /dashboard/settings/smtp — SMTP host/port/auth/from identity + test email action

Back-end endpoints are under /vendor/settings/*:

  • GET|PATCH /vendor/settings/receipt-template
  • GET|PATCH /vendor/settings/invoice-template
  • GET|PATCH /vendor/settings/smtp
  • POST /vendor/settings/smtp/test