View Source QRNBU.FieldLock (NBU payment QR v0.3.3)

Human-readable field lock bitmask builder for NBU QR Code V003.

Field lock controls which fields can be edited by the payer when processing a QR code payment. Setting a bit to 1 prevents the payer from modifying that field.

Quick Start

# Lock all fields except amount
iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount]) |> FieldLock.to_integer()
65279

# Get preset for fixed-amount payments
iex> alias QRNBU.FieldLock
iex> FieldLock.preset(:fixed_payment) |> FieldLock.to_integer()
65535

# Build custom lock
iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.lock([:recipient, :iban]) |> FieldLock.to_integer()
51455

NBU Requirement

Per NBU specification (Додаток 4, розділ IV, пункт 14), fields 1-5, 11, 14-17 must ALWAYS be locked. This module automatically enforces this requirement.

Field Reference

Field #NameEditableDescription
1service_markNoBCD marker
2format_versionNoVersion (003)
3encodingNoCharacter encoding
4functionNoUCT/ICT/XCT
5unique_recipient_idNoReserved
6recipientYesRecipient name
7ibanYesAccount number
8amountYesPayment amount
9recipient_codeYesEDRPOU/Tax ID
10category_purposeYesISO 20022 category
11referenceNoInvoice reference
12purposeYesPayment purpose text
13displayYesDisplay text
14field_lockNoThis field
15invoice_validityNoExpiration datetime
16invoice_creationNoCreation datetime

Summary

Functions

Creates a FieldLock with ALL fields locked.

Creates a FieldLock with all fields locked EXCEPT the specified ones.

Returns the bit position for a given field name.

Returns a human-readable description of the field lock.

Returns list of editable field names.

Returns list of all available field names.

Creates a FieldLock from an existing integer value.

Creates a FieldLock from an existing integer value, raising on error.

Locks the specified field(s) (adds to existing locks).

Checks if a specific field is locked.

Returns list of all currently locked fields.

Returns list of mandatory (always-locked) field names.

Creates a new FieldLock with only mandatory fields locked.

Creates a FieldLock with only mandatory fields locked, then adds the specified ones.

Returns a preset FieldLock for common payment scenarios.

Converts FieldLock to uppercase hex string (without 0x prefix).

Converts FieldLock to integer for use in QR data.

Unlocks the specified field(s) (removes from locks).

Checks if a specific field is unlocked (editable).

Returns list of all currently unlocked (editable) fields.

Types

@type field() ::
  :service_mark
  | :format_version
  | :encoding
  | :function
  | :unique_recipient_id
  | :recipient
  | :iban
  | :amount
  | :recipient_code
  | :category_purpose
  | :reference
  | :purpose
  | :display
  | :field_lock
  | :invoice_validity
  | :invoice_creation
@type preset_name() ::
  :minimal | :fixed_payment | :editable_amount | :editable_purpose | :flexible
@type t() :: %QRNBU.FieldLock{value: non_neg_integer()}

Functions

@spec all() :: t()

Creates a FieldLock with ALL fields locked.

Equivalent to 0xFFFF. The payer cannot modify any field.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.to_integer()
65535

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.to_hex()
"FFFF"
@spec all_except([field()]) :: t()

Creates a FieldLock with all fields locked EXCEPT the specified ones.

Mandatory fields remain locked regardless of the input. This is the most common way to create a field lock for payments where you want to allow the payer to edit specific fields.

Examples

# Allow only amount to be edited
iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount]) |> FieldLock.to_integer()
65279

# Allow amount and purpose to be edited
iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount, :purpose]) |> FieldLock.to_integer()
61183

# Attempting to unlock mandatory fields has no effect
iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:service_mark]) |> FieldLock.to_integer()
65535
@spec bit_position(field()) :: non_neg_integer() | nil

Returns the bit position for a given field name.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.bit_position(:amount)
8

iex> alias QRNBU.FieldLock
iex> FieldLock.bit_position(:service_mark)
1

iex> alias QRNBU.FieldLock
iex> FieldLock.bit_position(:unknown)
nil
@spec describe(t()) :: String.t()

Returns a human-readable description of the field lock.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.describe()
"0xFFFF: All fields locked"

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.describe()
"0xC83F: Editable fields: recipient, iban, amount, recipient_code, category_purpose, purpose, display"

iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount]) |> FieldLock.describe()
"0xFEFF: Editable fields: amount"
@spec editable_fields() :: [field()]

Returns list of editable field names.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.editable_fields()
[:recipient, :iban, :amount, :recipient_code, :category_purpose, :purpose, :display]
@spec fields() :: [field()]

Returns list of all available field names.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.fields() |> length()
16
@spec from_integer(integer()) :: {:ok, t()} | {:error, String.t()}

Creates a FieldLock from an existing integer value.

Returns {:ok, field_lock} if valid, or {:error, reason} if invalid. The value must be between 0 and 65535, and mandatory fields will be automatically locked if not already set.

Examples

iex> alias QRNBU.FieldLock
iex> {:ok, fl} = FieldLock.from_integer(0xFEFF)
iex> FieldLock.to_integer(fl)
65279

iex> alias QRNBU.FieldLock
iex> FieldLock.from_integer(70000)
{:error, "Value must be between 0 and 65535"}

iex> alias QRNBU.FieldLock
iex> {:ok, fl} = FieldLock.from_integer(0)
iex> FieldLock.to_integer(fl)
51263
@spec from_integer!(integer()) :: t()

Creates a FieldLock from an existing integer value, raising on error.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.from_integer!(0xFEFF) |> FieldLock.to_hex()
"FEFF"
@spec lock(t(), field() | [field()]) :: t()

Locks the specified field(s) (adds to existing locks).

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.lock(:amount) |> FieldLock.to_integer()
51519

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.lock([:amount, :recipient]) |> FieldLock.to_integer()
51583
Link to this function

locked?(field_lock, field)

View Source
@spec locked?(t(), field()) :: boolean()

Checks if a specific field is locked.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.locked?(:amount)
false

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.locked?(:service_mark)
true

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.locked?(:amount)
true
@spec locked_fields(t()) :: [field()]

Returns list of all currently locked fields.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.locked_fields()
[:invoice_creation, :service_mark, :format_version, :encoding, :function, :unique_recipient_id, :reference, :field_lock, :invoice_validity]

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.locked_fields() |> length()
16
@spec mandatory_fields() :: [field()]

Returns list of mandatory (always-locked) field names.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.mandatory_fields()
[:invoice_creation, :service_mark, :format_version, :encoding, :function, :unique_recipient_id, :reference, :field_lock, :invoice_validity]
@spec new() :: t()

Creates a new FieldLock with only mandatory fields locked.

This is the most permissive valid configuration - all editable fields can be modified by the payer.

Examples

iex> alias QRNBU.FieldLock
iex> fl = FieldLock.new()
iex> FieldLock.to_integer(fl)
51263

iex> alias QRNBU.FieldLock
iex> fl = FieldLock.new()
iex> FieldLock.to_hex(fl)
"C83F"
@spec only([field()]) :: t()

Creates a FieldLock with only mandatory fields locked, then adds the specified ones.

Use this when you want maximum payer flexibility but need to lock specific additional fields.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.only([:recipient, :iban]) |> FieldLock.to_integer()
51455

iex> alias QRNBU.FieldLock
iex> FieldLock.only([]) |> FieldLock.to_integer()
51263
@spec preset(preset_name()) :: t()

Returns a preset FieldLock for common payment scenarios.

Available Presets

  • :minimal - Only mandatory fields locked (maximum payer flexibility)
  • :fixed_payment - All fields locked (fixed invoice, no changes allowed)
  • :editable_amount - All except amount locked (common for variable payments)
  • :editable_purpose - All except purpose locked
  • :flexible - Mandatory + recipient info locked; amount, purpose, display editable

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.preset(:fixed_payment) |> FieldLock.to_hex()
"FFFF"

iex> alias QRNBU.FieldLock
iex> FieldLock.preset(:editable_amount) |> FieldLock.to_hex()
"FEFF"

iex> alias QRNBU.FieldLock
iex> FieldLock.preset(:minimal) |> FieldLock.to_hex()
"C83F"
@spec to_hex(t()) :: String.t()

Converts FieldLock to uppercase hex string (without 0x prefix).

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.to_hex()
"FFFF"

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.to_hex()
"C83F"

iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount]) |> FieldLock.to_hex()
"FEFF"
@spec to_integer(t()) :: non_neg_integer()

Converts FieldLock to integer for use in QR data.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.to_integer()
65535

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.to_integer()
51263
@spec unlock(t(), field() | [field()]) :: t()

Unlocks the specified field(s) (removes from locks).

Cannot unlock mandatory fields - they remain locked regardless.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.unlock(:amount) |> FieldLock.to_integer()
65279

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.unlock([:amount, :purpose]) |> FieldLock.to_integer()
61183

# Cannot unlock mandatory fields
iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.unlock(:service_mark) |> FieldLock.to_integer()
65535
@spec unlocked?(t(), field()) :: boolean()

Checks if a specific field is unlocked (editable).

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.unlocked?(:amount)
true

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.unlocked?(:amount)
false
@spec unlocked_fields(t()) :: [field()]

Returns list of all currently unlocked (editable) fields.

Examples

iex> alias QRNBU.FieldLock
iex> FieldLock.new() |> FieldLock.unlocked_fields()
[:recipient, :iban, :amount, :recipient_code, :category_purpose, :purpose, :display]

iex> alias QRNBU.FieldLock
iex> FieldLock.all() |> FieldLock.unlocked_fields()
[]

iex> alias QRNBU.FieldLock
iex> FieldLock.all_except([:amount]) |> FieldLock.unlocked_fields()
[:amount]