gobl

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 18, 2021 License: Apache-2.0 Imports: 12 Imported by: 37

README

GOBL

GOBL Logo

Go Business Language.

Introduction

GOBL (pronounced "gobble") is a hybrid solution between a document standard and a software library. On one side it defines a language for business documents in JSON, while at the same time providing a library and services written in Go to help build, validate, sign, and localise.

Traditional software business document standards consist of a set of definitions layered into different namespaces, followed by an independent set of implementations. A well written standard can be implemented anywhere and be compatible with any existing library. In practice however, and especially for XML-base standards, dealing with multiple namespaces adds a lot of complexity.

For GoBL a different approach is being taken. The code and library implementation is in itself the standard. Rather than trying to be flexible at the cost of complexity, GoBL includes everything needed for digital signatures, validation, and local implementations including tax definitions, all maintained under an open source license.

In our opinion, Go is an ideal language for this type of project due to its simple and concise syntax, performance, testing capabilities, and portability. We understand however that Go won't be everyone's cup of tea, so the project is designed to offer a server component (you could call it a microservice) to be launched in a docker container inside your own infrastructure, alongside a JSON Schema definition of all the key documents. Building your own documents and using the GoBL services to validate and sign them should be relatively straight forward.

GoBL Standard

Packages
Documents
Envelope

Serialization

Amounts & Percentages

Marshalling numbers can be problematic with JSON as the standard dictates that numbers should be represented as integers or floats, without any tailing 0s. This is fine for maths problems, but not useful when trying to convey monetary values or rates with a specific level of accuracy. GoBL will always serialise Amounts as strings to avoid any potential issues with number conversion.

ID vs UUID

Traditionally when dealing with databases a sequential ID is assigned to each tuple (document, row, item, etc.) starting from 1 going up to whatever the integer limit is. Creating a scalable and potentially global system however with regular numbers is not easy as you require a single point in the system responsible for ensuring that numbers are always provided in the correct order. Single points of failure are not good for scalability.

To get around the issues with sequential numbers, the UUID standard (Universally Unique IDentifier) was defined as a mechanism to create a very large number that can be potentially used to identify anything.

The GoBL library forces you to use UUIDs instead of sequential IDs for anything that could be referenced in the future. To enforce this fact, instead of naming fields id, we make an effort to call them uuid.

Sometimes sequential IDs are however required, usually for human consumption purposes such as ensuring order when generating invoices so that authorities can quickly check that dates and numbers are in order. Our recommendation for such codes is to use a dedicated service to generate sequential IDs based on the UUIDs, such as Invopop's Sequence Service.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoRegion is used when the envelope is missing a region.
	ErrNoRegion = NewError("no-region")

	// ErrValidation is used when a document fails a validation request.
	ErrValidation = NewError("validation")

	// ErrCalculation wraps around errors that we're generated during a
	// call to perform calculations on a document.
	ErrCalculation = NewError("calculation")

	// ErrMarshal is provided when there has been a problem attempting to encode
	// or marshal an object, usually into JSON.
	ErrMarshal = NewError("marshal")

	// ErrSignature identifies an issue related to signatures.
	ErrSignature = NewError("signature")

	// ErrInternal is a "catch-all" for errors that are not expected.
	ErrInternal = NewError("internal")
)

Functions

func Regions

func Regions() *region.Collection

Regions provides a region collection containing all the known region definitions.

Types

type Calculable

type Calculable interface {
	Calculate(r region.Region) error
}

Calculable defines the methods expected of a document payload that contains a `Calculate` method to be used to perform any additional calculations.

type Document

type Document interface {
	Type() string
}

Document defines what we expect from a document to be able to be included in an envelope.

type Envelope

type Envelope struct {
	Head       *Header           `json:"head" jsonschema:"title=Header,description=Details on what the contents are"`
	Document   *Payload          `json:"doc" jsonschema:"title=Document,description=The data being enveloped"`
	Signatures []*dsig.Signature `json:"sigs" jsonschema:"title=Signatures,description=JSON Web Signatures of the header"`
}

Envelope wraps around a gobl document and provides support for digest creation and digital signatures.

func NewEnvelope

func NewEnvelope(rc region.Code) *Envelope

NewEnvelope builds a new envelope object ready for data to be inserted and signed. If you are loading data from json, you can safely use a regular `new(Envelope)` call directly. A known region code is required as this will be used for any calculations and validations that need to be performed on the document to be inserted.

func (*Envelope) Extract

func (e *Envelope) Extract(doc Document) error

Extract the contents of the envelope into the provided document type.

func (*Envelope) Insert

func (e *Envelope) Insert(doc Document) error

Insert takes the provided document, performs any calculations, validates, then serializes it ready for use.

func (*Envelope) Region

func (e *Envelope) Region() region.Region

Region extracts the region from the header and provides a complete region object.

func (*Envelope) Sign

func (e *Envelope) Sign(key *dsig.PrivateKey) error

Sign uses the private key to the envelope headers.

func (*Envelope) Validate

func (e *Envelope) Validate() error

Validate ensures that the envelope contains everything it should to be considered valid GoBL.

func (*Envelope) Verify

func (e *Envelope) Verify() error

Verify ensures the digest headers still match the document contents.

type Error

type Error struct {
	Code  string `json:"code"`
	Cause error  `json:"cause"`
}

Error provides a structure to better be able to make error comparisons. The contents can also be serialised as JSON ready to send to a client if needed.

func NewError

func NewError(code string) *Error

NewError provides a new error with a code that is meant to provide a context.

func (*Error) Error

func (e *Error) Error() string

Error provides a string representation of the error.

func (*Error) Is

func (e *Error) Is(target error) bool

Is checks to see if the target error matches the current error or part of the chain.

func (*Error) WithCause

func (e *Error) WithCause(err error) *Error

WithCause is used to copy and add an underlying error to this one.

func (*Error) WithErrorf

func (e *Error) WithErrorf(format string, a ...interface{}) *Error

WithErrorf wraps around the `fmt.Errorf` call to provide a more meaningful error in the context.

type Header struct {
	UUID   uuid.UUID    `json:"uuid" jsonschema:"title=UUID,description=Unique UUIDv1 identifier for the envelope."`
	Type   string       `json:"typ" jsonschema:"title=Type,description=Body type of the document contents."`
	Region region.Code  `json:"rgn" jsonschema:"title=Region,description=Code for the region the document should be validated with."`
	Digest *dsig.Digest `json:"dig" jsonschema:"title=Digest,description=Digest of the canonical JSON body."`
	Stamps []*Stamp     `json:"stamps,omitempty" jsonschema:"title=Stamps,description=Seals of approval from other organisations."`
	Tags   []string     `json:"tags,omitempty" jsonschema:"title=Tags,description=Set of labels that describe but have no influence on the data."`
	Meta   org.Meta     `json:"meta,omitempty" jsonschema:"title=Meta,description=Additional semi-structured information about this envelope."`
	Notes  string       `` /* 133-byte string literal not displayed */
	Draft  bool         `` /* 162-byte string literal not displayed */
}

Header defines the meta data of the body. The header is used as the payload for the JSON Web Signatures, so we want this to be as compact as possible.

func NewHeader

func NewHeader(rc region.Code) *Header

NewHeader creates a new header and automatically assigns a UUIDv1.

func (*Header) Validate

func (h *Header) Validate() error

Validate checks that the header contains the basic information we need to function.

type Payload

type Payload struct {
	// contains filtered or unexported fields
}

Payload helps us handle the document's contents by essentially wrapping around the json RawMessage.

func (Payload) JSONSchemaType

func (Payload) JSONSchemaType() *jsonschema.Type

func (*Payload) MarshalJSON

func (p *Payload) MarshalJSON() ([]byte, error)

func (*Payload) UnmarshalJSON

func (p *Payload) UnmarshalJSON(data []byte) error

type Signatures

type Signatures []*dsig.Signature

Signatures keeps together a list of signatures that we're used to sign the document head contents.

type Stamp

type Stamp struct {
	Provider string `json:"prv" jsonschema:"title=Provider,description=Identity of the agency used to create the stamp"`
	Value    string `json:"val" jsonschema:"title=Value,description=The serialized stamp value generated for or by the external agency"`
}

Stamp defines an official seal of approval from a third party like a governmental agency or intermediary and should thus be included in any official envelopes.

type Validatable

type Validatable interface {
	Validate(r region.Region) error
}

Validatable describes a document that can be validated.

Directories

Path Synopsis
examples
es
internal
regions
es
gb

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL