Platform

Numbered invoices for GoCommerce

A Go package that issues an invoice, with its own gapless number, the moment an order is paid — and keeps a snapshot of the order as it was. The seller’s name, address and tax id are its settings, set in code.

What it does

The module listens for order.paid and issues an invoice for that order: a number from its own per-year sequence, the order’s total and currency, and a snapshot of the whole order as it stood. The admin gets an Invoices screen and a link from each order; the API gets two read-only routes.

The package doc explains why it is a module at all: an order is what a shopper asked for, while an invoice is an accounting document with its own gapless sequence and its own retention rules. Keeping them apart is why this is a module and not a column on the order.

It needs no third party and talks to nothing outside the store. It prints a tax id beside the seller — a GSTIN, a VAT number, whatever applies — and serves the invoice as a page to print or as JSON.

Configuration

One setting is required: the seller’s name. All five are Config fields — this module has no settings in the admin’s panel — so they are set in your own main(), or through the reference binary’s -invoices flag and its three environment variables.

Invoices module settings — 5 settings, 1 required
SettingEnvironment variableRequiredWhat it does
SellerName INVOICES_SELLER_NAME Yes Heads the document. Without it the module refuses to register and the store does not start; the reference binary falls back to “This store”.
SellerAddress INVOICES_SELLER_ADDRESS No Printed under the seller’s name. The code’s comment calls it required, but only the name is checked.
TaxID INVOICES_TAX_ID No Printed beside the seller, for jurisdictions that need it — a GSTIN, a VAT number, whatever applies.
NumberFormat — No Builds the number from {year} and {seq}; {seq:05} pads to five digits. Defaults to INV-{year}-{seq:05}.
Footer — No Printed at the bottom of every invoice.

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

Setting it up

  1. Choose the number formatThe default is INV-{year}-{seq:05}, which gives INV-2026-00001. Settle yours before the first invoice; the count restarts at 1 each calendar year.
  2. Install the moduleImport it and pass invoices.New to gocommerce.New with at least SellerName, as below — or run the reference binary with -invoices and the INVOICES_ variables.
  3. Take a paymentEvery order that reaches paid gets its invoice. On start-up, paid orders that have none are caught up.
  4. Print or fetch itOpen it from the order or the Invoices screen in the admin, or GET /api/admin/x/invoices/{orderId} — HTML to print, or JSON with Accept: application/json.

main.go

import (
	"github.com/itswadesh/gocommerce/core"
	"github.com/itswadesh/gocommerce/ext/invoices"
)

app, err := gocommerce.New(cfg,
	invoices.New(invoices.Config{
		SellerName:   "Example Ltd",
		NumberFormat: "INV-{year}-{seq:05}",
	}),
)

The package doc’s own example, with its imports; cfg is your gocommerce.Config. Add SellerAddress, TaxID and Footer the same way. Import path github.com/itswadesh/gocommerce/ext/invoices.

How it works

What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

FAQ

Questions about the Invoices module

Is it a GST invoice?

It prints your GSTIN if you set TaxID, and nothing more GST-specific: no HSN or SAC codes, no place of supply, no CGST, SGST or IGST lines — and, as it stands, no tax line at all. Whether a document meets your invoicing rules is a question for your accountant; the template in invoices.go is where to change it.

When does a cash-on-delivery order get its invoice?

When it is marked paid, because the module listens for order.paid. A cash-on-delivery order has no invoice until an operator records the payment.

Can the numbering have gaps?

Not from this module. The counter is taken under a row lock inside the transaction that writes the invoice, so a failed write gives its number back. The count restarts at 1 each calendar year, in UTC.

Does the shopper receive the invoice?

Not from this module. It issues the invoice, stores it and serves it to the admin and the API; sending it to the shopper is not in the code.

Why is an invoice not just a field on the order?

The package doc answers it: an order is what a shopper asked for, while an invoice is an accounting document with its own gapless sequence and its own retention rules. Keeping them apart is why this is a module.

Source

Everything on this page is read from ext/invoices in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.

Try it against a store of your own

The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.

Chat on WhatsApp