Latest PyPI version Github actions test pipeline status Read the Docs build status Test coverage

netsgiro

netsgiro is a Python library for working with Nets AvtaleGiro and OCR Giro files.

AvtaleGiro is a direct debit solution that is in widespread use in Norway, with more than 15 000 companies offering it to their customers. OCR Giro is used by Nets and Norwegian banks to update payees on recent deposits to their bank accounts. In combination, AvtaleGiro and OCR Giro allows for a high level of automation of invoicing and payment processing.

The netsgiro library supports:

  • Parsing AvtaleGiro agreements
  • Creating AvtaleGiro payment requests
  • Creating AvtaleGiro cancellations
  • Parsing OCR Giro transactions

netsgiro is available from PyPI. To install it, run:

pip install netsgiro

For details and code examples, see the netsgiro documentation.

For further details, please refer to the official AvtaleGiro and OCR Giro documentation from Nets.

License

Copyright 2017-2019 Otovo AS. Licensed under the Apache License, Version 2.0. See the LICENSE file for the full license text.

Project resources

Quickstart

The following examples takes you through the flow from payers signing up for AvtaleGiro to receiving payments.

See the objects API reference for details on the API and data fields used in the examples below.

Parsing AvtaleGiro agreements

To parse an OCR file, you must first read the contents of the OCR file. It should be decoded using the ISO-8859-1 encoding:

with open('my-ocr-file.txt', 'r', encoding='iso-8859-1') as fh:
    data = fh.read()

For the purpose of the following example we use the following input data:

>>> data = """
... NY000010000080801091949000102000000000000000000000000000000000000000000000000000
... NY212420000000000000000299991042764000000000000000000000000000000000000000000000
... NY21947000000011          000112000507155J00000000000000000000000000000000000000
... NY21947000000021          001006300507304N00000000000000000000000000000000000000
... NY21947000000031          001020200507462J00000000000000000000000000000000000000
... NY21947000000041          001026300507518J00000000000000000000000000000000000000
... NY21947000000051          001044400507783J00000000000000000000000000000000000000
... NY21947000000061          001045000507792N00000000000000000000000000000000000000
... NY21947000000071          001057800507922N00000000000000000000000000000000000000
... NY21947000000081          001060300509570J00000000000000000000000000000000000000
... NY21947000000091          001087600508176J00000000000000000000000000000000000000
... NY21947000000101          001105600508416J00000000000000000000000000000000000000
... NY21947000000111          001123000508621J00000000000000000000000000000000000000
... NY21947000000121          001124000508637J00000000000000000000000000000000000000
... NY21947000000131          001138900509107N00000000000000000000000000000000000000
... NY21947000000141          001143700509281J00000000000000000000000000000000000000
... NY21947000000151          001146800509317J00000000000000000000000000000000000000
... NY21947000000161          001186100509492N00000000000000000000000000000000000000
... NY212488000000160000001800000000000000000000000000000000000000000000000000000000
... NY000089000000160000002000000000000000000190417000000000000000000000000000000000
... """.strip()

netsgiro.parse() parses the input and returns a netsgiro.Transmission object:

>>> import netsgiro
>>> transmission = netsgiro.parse(data)
>>> transmission
Transmission(number='1091949', data_transmitter='00008080', data_recipient='00010200', date=datetime.date(2017, 4, 19))
>>> transmission.number
'1091949'

Each transmission can contain any number of assignments:

>>> len(transmission.assignments)
1
>>> assignment = transmission.assignments[0]
>>> assignment
Assignment(service_code=<ServiceCode.AVTALEGIRO: 21>, type=<AssignmentType.AVTALEGIRO_AGREEMENTS: 24>, number='0000002', account='99991042764', agreement_id=None, date=None)
>>> assignment.number
'0000002'

Each assignment can contain any number of transactions, in this case AvtaleGiro agreement updates:

>>> pprint(assignment.transactions)
[Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=1, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='000112000507155', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=2, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001006300507304', notify=False),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=3, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001020200507462', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=4, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001026300507518', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=5, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001044400507783', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=6, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001045000507792', notify=False),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=7, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001057800507922', notify=False),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=8, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001060300509570', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=9, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001087600508176', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=10, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001105600508416', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=11, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001123000508621', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=12, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001124000508637', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=13, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001138900509107', notify=False),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=14, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001143700509281', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=15, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001146800509317', notify=True),
 Agreement(service_code=<ServiceCode.AVTALEGIRO: 21>, number=16, registration_type=<AvtaleGiroRegistrationType.NEW_OR_UPDATED_AGREEMENT: 1>, kid='001186100509492', notify=False)]

As you can see, all updates here are for new or updated agreements. If a payer deletes their AvtaleGiro agreement the registration_type field will be DELETED_AGREEMENT.

Building payment requests

Once you have established AvtaleGiro agreements with some payers, you can start creating payment requests based on your invoices.

You start by creating a Transmission with Nets as the recipient:

>>> from datetime import date
>>> from decimal import Decimal
>>> import netsgiro
>>> transmission = netsgiro.Transmission(
...     number='1703231',
...     data_transmitter='01234567',
...     data_recipient=netsgiro.NETS_ID)

Then, add an AvtaleGiro transaction assignment to the transmission using add_assignment():

>>> assignment = transmission.add_assignment(
...     service_code=netsgiro.ServiceCode.AVTALEGIRO,
...     assignment_type=netsgiro.AssignmentType.TRANSACTIONS,
...     number='0323001',
...     account='99998877777')

For each invoice, add a payment requests to the assignment using add_payment_request():

>>> payment_request = assignment.add_payment_request(
...     kid='000133700501645',
...     due_date=date(2017, 4, 6),
...     amount=Decimal('5244.63'),
...     reference='ACME invoice #50164',
...     payer_name='Wonderland',
...     bank_notification=None)

Finally, you can write out the OCR data using to_ocr():

>>> data = transmission.to_ocr()
>>> print(data)
NY000010012345671703231000080800000000000000000000000000000000000000000000000000
NY210020000000000032300199998877777000000000000000000000000000000000000000000000
NY2102300000001060417           00000000000524463          000133700501645000000
NY2102310000001Wonderland                         ACME invoice #50164      00000
NY210088000000010000000400000000000524463060417060417000000000000000000000000000
NY000089000000010000000600000000000524463060417000000000000000000000000000000000

Before delivering the transmission to Nets, remember to encode it using the ISO-8859-1 encoding to correctly preserve Norwegian letters.

To encode a bytestring with the correct encoding:

encoded_data = data.encode('iso-8859-1')

To save the result a file with the correct encoding:

with open('my-ocr-file.txt', 'wt', encoding='iso-8859-1') as fh:
    fh.write(data)

Building payment cancellations

To cancel one or more AvtaleGiro payment requests, the process is very similar to creating payment requests. You start with a Transmission:

>>> from datetime import date
>>> from decimal import Decimal
>>> import netsgiro
>>> transmission = netsgiro.Transmission(
...     number='1703232',
...     data_transmitter='01234567',
...     data_recipient=netsgiro.NETS_ID)

Add a AvtaleGiro cancellation assignment to the transmission using add_assignment():

>>> assignment = transmission.add_assignment(
...     service_code=netsgiro.ServiceCode.AVTALEGIRO,
...     assignment_type=netsgiro.AssignmentType.AVTALEGIRO_CANCELLATIONS,
...     number='0323002',
...     account='99998877777')

Add one or more payment cancellations to the assignment using add_payment_cancellation():

>>> transaction = assignment.add_payment_cancellation(
...     kid='000133700501645',
...     due_date=date(2017, 4, 6),
...     amount=Decimal('5244.63'),
...     reference='ACME invoice #50164',
...     payer_name='Wonderland',
...     bank_notification=None)

The arguments passed to add_payment_cancellation() must be identical to the arguments passed to add_payment_request() when creating the payment request you are now cancelling.

Finally, you can write out the OCR data using to_ocr() and write the result to a file.

>>> data = transmission.to_ocr()
>>> print(data)
NY000010012345671703232000080800000000000000000000000000000000000000000000000000
NY213620000000000032300299998877777000000000000000000000000000000000000000000000
NY2193300000001060417           00000000000524463          000133700501645000000
NY2193310000001Wonderland                         ACME invoice #50164      00000
NY213688000000010000000400000000000524463060417060417000000000000000000000000000
NY000089000000010000000600000000000524463060417000000000000000000000000000000000

Parsing OCR Giro transactions

When receiving payments to your bank account, you’ll receive OCR Giro files with lists of all the transactions.

For the purpose of the following example we use the following input data:

>>> data = """
... NY000010000080800170031000102000000000000000000000000000000000000000000000000000
... NY090020001008566000000299991042764000000000000000000000000000000000000000000000
... NY09213000000012001921320101464000000000000102000                  0000531000000
... NY092131000000196368271940990385620000000160192999905123410000000000000000000000
... NY0921320000001Foo bar baz                             0000000000000000000000000
... NY09103000000022001921320101464000000000000102000                  0036633000000
... NY091031000000297975960160975960161883206160192999910055240000000000000000000000
... NY09103000000032001921320101464000000000000056000                  0048763000000
... NY091031000000397975816750975816751883206170192999910427640000000000000000000000
... NY09103000000042001921320101464000000000000102000                  0063851000000
... NY091031000000497975857600975857601883206170192999910055240000000000000000000000
... NY09103000000052001921320101464000000000000102000                  0120243000000
... NY091031000000597975915790975915791883206170192999905678980000000000000000000000
... NY09103000000062001921320101464000000000000056000                  0165867000000
... NY091031000000697975851090975851091883206160192999910427640000000000000000000000
... NY09103000000072001921320101464000000000000102000                  0178357000000
... NY091031000000797975848550975848551883206160192999910055240000000000000000000000
... NY09113000000082001921320101570000000000000150000           02212291038306000000
... NY091131000000896000000006001553800000000200192999995455280000000000000000000000
... NY09123000000092001921320101570000000000000120000           02311291038304000000
... NY091231000000900000000000010201690000000000000999910154060000000000000000000000
... NY09133000000102001921320101570000000000000055000           02310291038308000000
... NY091331000001000000000002206638000000000000000999910175300000000000000000000000
... NY09113000000112001921320231570000000000000194300                000149012000000
... NY091131000001196000000006001552110000000200192999905080340000000000000000000000
... NY09123000000122001921320231570000000000000100000           01211291038306000000
... NY091231000001200000000000010201690000000000000999905080340000000000000000000000
... NY09133000000132001921320231570000000000000050000           02111291038305000000
... NY091331000001300000000000320663700000000000000999905230840000000000000000000000
... NY09133000000142001921320335572000000000002050000           02321291038303000000
... NY091331000001400000000000250663700000000000000999905100550000000000000000000000
... NY09133000000152001921320941570000000000000550000           02331291038302000000
... NY091331000001500000000007974896260000000000000999910111250000000000000000000000
... NY09133000000162001921320941570000000000001050000           02341291038301000000
... NY091331000001600000000000281163700000000000000999995455280000000000000000000000
... NY09103000000172001921320941570000000000000064400           02358291038305000000
... NY091031000001700000000002806638000000000000000999995455280000000000000000000000
... NY09103000000182001921320904514000000000000056400           02311291029238000000
... NY091031000001896367780030913067140000000170192999910154060000000000000000000000
... NY09103000000192001921320904514000000000000028800           02311291034832000000
... NY091031000001996367778210976949990000000160192999910080340000000000000000000000
... NY09103000000202001921320904514000000000000054000           02311291133188000000
... NY091031000002096367781170913088610000000170192999910111250000000000000000000000
... NY090088000000200000004300000000005144900200192200192200192000000000000000000000
... NY000089000000200000004500000000005144900200192000000000000000000000000000000000
... """.strip()

netsgiro.parse() parses the input and returns a netsgiro.Transmission object:

>>> import netsgiro
>>> transmission = netsgiro.parse(data)
>>> transmission
Transmission(number='0170031', data_transmitter='00008080', data_recipient='00010200', date=datetime.date(1992, 1, 20))
>>> transmission.number
'0170031'

Each transmission can contain any number of assignments:

>>> len(transmission.assignments)
1
>>> assignment = transmission.assignments[0]
>>> assignment
Assignment(service_code=<ServiceCode.OCR_GIRO: 9>, type=<AssignmentType.TRANSACTIONS: 0>, number='0000002', account='99991042764', agreement_id='001008566', date=datetime.date(1992, 1, 20))
>>> assignment.number
'0000002'
>>> assignment.get_earliest_transaction_date()
datetime.date(1992, 1, 20)
>>> assignment.get_latest_transaction_date()
datetime.date(1992, 1, 20)
>>> assignment.get_total_amount()
Decimal('51449')

Each assignment can contain any number of transactions, in this case OCR Giro payment transactions:

>>> pprint(assignment.transactions)
[Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.PURCHASE_WITH_TEXT: 21>, number=1, date=datetime.date(1992, 1, 20), amount=Decimal('1020'), kid='0000531', reference='099038562', text='Foo bar baz', centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9636827194', bank_date=datetime.date(1992, 1, 16), debit_account='99990512341', _filler='0000000'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=2, date=datetime.date(1992, 1, 20), amount=Decimal('1020'), kid='0036633', reference='097596016', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797596016', bank_date=datetime.date(1992, 1, 16), debit_account='99991005524', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=3, date=datetime.date(1992, 1, 20), amount=Decimal('560'), kid='0048763', reference='097581675', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797581675', bank_date=datetime.date(1992, 1, 17), debit_account='99991042764', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=4, date=datetime.date(1992, 1, 20), amount=Decimal('1020'), kid='0063851', reference='097585760', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797585760', bank_date=datetime.date(1992, 1, 17), debit_account='99991005524', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=5, date=datetime.date(1992, 1, 20), amount=Decimal('1020'), kid='0120243', reference='097591579', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797591579', bank_date=datetime.date(1992, 1, 17), debit_account='99990567898', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=6, date=datetime.date(1992, 1, 20), amount=Decimal('560'), kid='0165867', reference='097585109', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797585109', bank_date=datetime.date(1992, 1, 16), debit_account='99991042764', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=7, date=datetime.date(1992, 1, 20), amount=Decimal('1020'), kid='0178357', reference='097584855', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01464', sign='0', form_number='9797584855', bank_date=datetime.date(1992, 1, 16), debit_account='99991005524', _filler='1883206'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_STANDING_ORDERS: 11>, number=8, date=datetime.date(1992, 1, 20), amount=Decimal('1500'), kid='02212291038306', reference='600155380', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01570', sign='0', form_number='9600000000', bank_date=datetime.date(1992, 1, 20), debit_account='99999545528', _filler='0000000'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_DIRECT_REMITTANCE: 12>, number=9, date=datetime.date(1992, 1, 20), amount=Decimal('1200'), kid='02311291038304', reference='001020169', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01570', sign='0', form_number='0000000000', bank_date=None, debit_account='99991015406', _filler='0000000'),
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_BUSINESS_TERMINAL_GIRO: 13>, number=10, date=datetime.date(1992, 1, 20), amount=Decimal('550'), kid='02310291038308', reference='220663800', text=None, centre_id='13', day_code=20, partial_settlement_number=1, partial_settlement_serial_number='01570', sign='0', form_number='0000000000', bank_date=None, debit_account='99991017530', _filler='0000000'),
 ...
 Transaction(service_code=<ServiceCode.OCR_GIRO: 9>, type=<TransactionType.FROM_GIRO_DEBITED_ACCOUNT: 10>, number=20, date=datetime.date(1992, 1, 20), amount=Decimal('540'), kid='02311291133188', reference='091308861', text=None, centre_id='13', day_code=20, partial_settlement_number=9, partial_settlement_serial_number='04514', sign='0', form_number='9636778117', bank_date=datetime.date(1992, 1, 17), debit_account='99991011125', _filler='0000000')]

Objects API

The objects API is the higher level API. It parses the “OCR” file into an object structure that for most use cases is more pleasant to work with than the lower level records API.

See the quickstart guide for an introduction to how to use the API.

netsgiro.parse(data: str) → netsgiro.objects.Transmission[source]

Parse an OCR file into a Transmission object.

Transmission class

class netsgiro.Transmission(number: str, data_transmitter: str, data_recipient: str, date: Optional[datetime.date] = None, assignments: List[Assignment] = NOTHING)[source]

Transmission is the top-level object.

An OCR file contains a single transmission. The transmission can contain multiple Assignment objects of various types.

add_assignment(*, service_code: netsgiro.enums.ServiceCode, assignment_type: netsgiro.enums.AssignmentType, agreement_id: Optional[str] = None, number: str, account: str, date: Optional[datetime.date] = None) → netsgiro.objects.Assignment[source]

Add an assignment to the transmission.

classmethod from_records(records: List[R]) → netsgiro.objects.Transmission[source]

Build a Transmission object from a list of record objects.

get_num_records() → int[source]

Get number of records in the transmission.

Includes the transmission’s start and end record.

get_num_transactions() → int[source]

Get number of transactions in the transmission.

get_total_amount() → decimal.Decimal[source]

Get the total amount from all transactions in the transmission.

to_ocr() → str[source]

Convert the transmission to an OCR string.

to_records() → Iterable[Record][source]

Convert the transmission to a list of records.

assignments

List of assignments.

data_recipient

Data recipient’s Nets ID. String of 8 digits.

data_transmitter

Data transmitter’s Nets ID. String of 8 digits.

date

For OCR Giro files from Nets, this is Nets’ processing date.

For AvtaleGiro payment request, the earliest due date in the transmission is automatically used.

number

Data transmitters unique enumeration of the transmission. String of 7 digits.

Assignment class

class netsgiro.Assignment(service_code: Union[netsgiro.enums.ServiceCode, int, str], type: Union[netsgiro.enums.AssignmentType, int, str], number: str, account: str, agreement_id: Optional[str] = None, date: Optional[datetime.date] = None, transactions: List[Union[Transaction, Agreement, PaymentRequest]] = NOTHING)[source]

An Assignment groups multiple transactions within a transmission.

Use netsgiro.Transmission.add_assignment() to create assignments.

add_payment_cancellation(*, kid: str, due_date: datetime.date, amount: decimal.Decimal, reference: Optional[str] = None, payer_name: Optional[str] = None, bank_notification: Union[bool, str] = False) → netsgiro.objects.PaymentRequest[source]

Add an AvtaleGiro cancellation to the assignment.

The assignment must have service code AVTALEGIRO and assignment type AVTALEGIRO_CANCELLATIONS.

Otherwise, the cancellation must be identical to the payment request it is cancelling.

add_payment_request(*, kid: str, due_date: datetime.date, amount: decimal.Decimal, reference: Optional[str] = None, payer_name: Optional[str] = None, bank_notification: Union[bool, str] = False, validate_due_date: bool = False) → netsgiro.objects.PaymentRequest[source]

Add an AvtaleGiro payment request to the assignment.

The assignment must have service code AVTALEGIRO and assignment type TRANSACTIONS.

classmethod from_records(records: List[R]) → netsgiro.objects.Assignment[source]

Build an Assignment object from a list of record objects.

get_earliest_transaction_date() → Optional[datetime.date][source]

Get earliest date from the assignment’s transactions.

get_latest_transaction_date() → Optional[datetime.date][source]

Get latest date from the assignment’s transactions.

get_num_records() → int[source]

Get number of records in the assignment.

Includes the assignment’s start and end record.

get_num_transactions() → int[source]

Get number of transactions in the assignment.

get_total_amount() → decimal.Decimal[source]

Get the total amount from all transactions in the assignment.

to_records() → Iterable[Union[netsgiro.records.AssignmentStart, netsgiro.records.TransactionAmountItem1, netsgiro.records.TransactionAmountItem2, netsgiro.records.TransactionAmountItem3, netsgiro.records.AssignmentEnd]][source]

Convert the assignment to a list of records.

account

The payee’s bank account. String of 11 digits.

agreement_id

Used for OCR Giro.

The payee’s agreement ID with Nets. String of 9 digits.

date

Used for OCR Giro.

The date the assignment was generated by Nets.

number

The assignment number. String of 7 digits.

service_code

The service code. One of ServiceCode.

transactions

List of transaction objects, like Agreement, PaymentRequest, Transaction.

type

The transaction type. One of TransactionType.

Transaction classes

class netsgiro.Agreement(service_code: Union[netsgiro.enums.ServiceCode, int, str], number: int, registration_type: Union[netsgiro.enums.AvtaleGiroRegistrationType, int, str], kid: Optional[str], notify: bool)[source]

Agreement contains an AvtaleGiro agreement update.

Agreements are only found in assignments of the AVTALEGIRO_AGREEMENTS type, which are only created by Nets.

classmethod from_records(records: List[netsgiro.records.AvtaleGiroAgreement]) → netsgiro.objects.Agreement[source]

Build an Agreement object from a list of record objects.

to_records() → Iterable[netsgiro.records.AvtaleGiroAgreement][source]

Convert the agreement to a list of records.

TRANSACTION_TYPE = 94
kid

KID number to identify the customer and invoice.

notify

Whether the payer wants notification about payment requests.

number

Transaction number. Unique and ordered within an assignment.

registration_type

Type of agreement registration update. One of AvtaleGiroRegistrationType.

service_code

The service code. One of ServiceCode.

class netsgiro.PaymentRequest(service_code: Union[netsgiro.enums.ServiceCode, int, str], type: Union[netsgiro.enums.TransactionType, int, str], number: int, date: datetime.date, amount, kid: Optional[str], reference: Optional[str], text: Optional[str], payer_name: Optional[str])[source]

PaymentRequest contains an AvtaleGiro payment request or cancellation.

To create a transaction, you will normally use the helper methods on Assignment: add_payment_request() and add_payment_cancellation().

classmethod from_records(records: List[TR]) → netsgiro.objects.PaymentRequest[source]

Build a Transaction object from a list of record objects.

to_records() → Iterable[Union[netsgiro.records.TransactionAmountItem1, netsgiro.records.TransactionAmountItem2, netsgiro.records.TransactionSpecification]][source]

Convert the transaction to a list of records.

amount

Transaction amount in NOK with two decimals.

amount_in_cents

Transaction amount in NOK cents.

date

The due date.

kid

KID number to identify the customer and invoice.

number

Transaction number. Unique and ordered within an assignment.

payer_name

The value is only used to help the payee cross-reference reports from Nets with their own records. It is not vi.attr.ible to the payer.

reference

This is a specification line that will, if set, be displayed on the payers account statement. Alphanumeric, max 25 chars.

service_code

The service code. One of ServiceCode.

text

This is up to 42 lines of 80 chars each of free text used by the bank to notify the payer about the payment request. It is not used if the payee is responsible for notifying the payer.

type

The transaction type. One of TransactionType.

class netsgiro.Transaction(service_code: Union[netsgiro.enums.ServiceCode, int, str], type: Union[netsgiro.enums.TransactionType, int, str], number: int, date: datetime.date, amount, kid: Optional[str], reference: Optional[str], text: Optional[str], centre_id: Optional[str], day_code: Optional[int], partial_settlement_number: Optional[int], partial_settlement_serial_number: Optional[str], sign: Optional[str], form_number: Optional[str], bank_date: Optional[datetime.date], debit_account: Optional[str], filler: Optional[str])[source]

Transaction contains an OCR Giro transaction.

Transactions are found in assignments with the service code OCR_GIRO type, which are only created by Nets.

classmethod from_records(records: List[TR]) → netsgiro.objects.Transaction[source]

Build a Transaction object from a list of record objects.

to_records() → Iterable[Union[netsgiro.records.TransactionAmountItem1, netsgiro.records.TransactionAmountItem2, netsgiro.records.TransactionAmountItem3]][source]

Convert the transaction to a list of records.

amount

Transaction amount in NOK with two decimals.

amount_in_cents

Transaction amount in NOK cents.

bank_date

Used for OCR Giro.

centre_id

Used for OCR Giro.

date

Nets’ processing date.

day_code

Used for OCR Giro.

debit_account

Used for OCR Giro.

form_number

Used for OCR Giro.

kid

KID number to identify the customer and invoice.

number

Transaction number. Unique and ordered within an assignment.

partial_settlement_number

Used for OCR Giro.

partial_settlement_serial_number

Used for OCR Giro.

reference

The value depends on the payment method.

service_code

The service code. One of ServiceCode.

sign

Used for OCR Giro.

text

Up to 40 chars of free text from the payment terminal.

type

The transaction type. One of TransactionType.

Records API

The records API is the lower level API. It parses each line of “OCR” text input into a record object. A record object also knows about its OCR representation.

For most use cases, the objects API is preferable to the records API.

File parsing

To parse an OCR file, you must first read the contents of the OCR file. It should be decoded using the ISO-8559-1 encoding:

with open('my-ocr-file.txt', 'r', encoding='iso-8859-1') as fh:
    data = fh.read()

For the purpose of the following example we use the following input data:

>>> data = '''
... NY000010555555551000081000080800000000000000000000000000000000000000000000000000
... NY210020000000000400008688888888888000000000000000000000000000000000000000000000
... NY2121300000001170604           00000000000000100          008000011688373000000
... NY2121310000001NAVN                                                        00000
... NY212149000000140011 Gjelder Faktura: 168837  Dato: 19/03/0400000000000000000000
... NY212149000000140012                  ForfallsDato: 17/06/0400000000000000000000
... NY2121300000002170604           00000000000000100          008000021688389000000
... NY2121310000002NAVN                                                        00000
... NY212149000000240011 Gjelder Faktura: 168838  Dato: 19/03/0400000000000000000000
... NY212149000000240012                  ForfallsDato: 17/06/0400000000000000000000
... NY2121300000003170604           00000000000000100          008000031688395000000
... NY2121310000003NAVN                                                        00000
... NY2121300000004170604           00000000000000100          008000041688401000000
... NY2121310000004NAVN                                                        00000
... NY2121300000005170604           00000000000000100          008000051688416000000
... NY2121310000005NAVN                                                        00000
... NY212149000000540011 Gjelder Faktura: 168841  Dato: 19/03/0400000000000000000000
... NY212149000000540012                  ForfallsDato: 17/06/0400000000000000000000
... NY2102300000006170604           00000000000000100          008000061688422000000
... NY2102310000006NAVN                                                        00000
... NY210088000000060000002000000000000000600170604170604000000000000000000000000000
... NY000089000000060000002200000000000000600170604000000000000000000000000000000000
... '''.strip()  # noqa

netsgiro.records.parse() parses the input and returns a record object for each line of input:

>>> import netsgiro.records
>>> records = netsgiro.records.parse(data)
>>> len(records)
22
>>> pprint(records)
[TransmissionStart(service_code=<ServiceCode.NONE: 0>, transmission_number='1000081', data_transmitter='55555555', data_recipient='00008080'),
 AssignmentStart(service_code=<ServiceCode.AVTALEGIRO: 21>, assignment_type=<AssignmentType.TRANSACTIONS: 0>, assignment_number='4000086', assignment_account='88888888888', agreement_id='000000000'),
 TransactionAmountItem1(service_code=<ServiceCode.AVTALEGIRO: 21>, transaction_type=<TransactionType.PURCHASE_WITH_TEXT: 21>, transaction_number=1, nets_date=datetime.date(2004, 6, 17), amount=100, kid='008000011688373', centre_id=None, day_code=None, partial_settlement_number=None, partial_settlement_serial_number=None, sign=None),
 TransactionAmountItem2(service_code=<ServiceCode.AVTALEGIRO: 21>, transaction_type=<TransactionType.PURCHASE_WITH_TEXT: 21>, transaction_number=1, reference=None, form_number=None, bank_date=None, debit_account=None, _filler=None, payer_name='NAVN'),
 TransactionSpecification(service_code=<ServiceCode.AVTALEGIRO: 21>, transaction_type=<TransactionType.PURCHASE_WITH_TEXT: 21>, transaction_number=1, line_number=1, column_number=1, text=' Gjelder Faktura: 168837  Dato: 19/03/04'),
 TransactionSpecification(service_code=<ServiceCode.AVTALEGIRO: 21>, transaction_type=<TransactionType.PURCHASE_WITH_TEXT: 21>, transaction_number=1, line_number=1, column_number=2, text='                  ForfallsDato: 17/06/04'),
 ...
 AssignmentEnd(service_code=<ServiceCode.AVTALEGIRO: 21>, assignment_type=<AssignmentType.TRANSACTIONS: 0>, num_transactions=6, num_records=20, total_amount=600, nets_date_1=datetime.date(2004, 6, 17), nets_date_2=datetime.date(2004, 6, 17), nets_date_3=None),
 TransmissionEnd(service_code=<ServiceCode.NONE: 0>, num_transactions=6, num_records=22, total_amount=600, nets_date=datetime.date(2004, 6, 17))]
netsgiro.records.parse(data: str) → List[R][source]

Parse an OCR file into a list of record objects.

Record types

Given a record object, all record fields are available as sensible Python types:

>>> assignment_end = records[-2]
>>> assignment_end.service_code
<ServiceCode.AVTALEGIRO: 21>
>>> assignment_end.RECORD_TYPE
<RecordType.ASSIGNMENT_END: 88>
>>> assignment_end.assignment_type
<AssignmentType.TRANSACTIONS: 0>
>>> assignment_end.nets_date_earliest
datetime.date(2004, 6, 17)
>>> assignment_end.nets_date_latest
datetime.date(2004, 6, 17)
>>> assignment_end.num_records
20
>>> assignment_end.num_transactions
6
>>> assignment_end.total_amount
600

You can also convert the record back to an OCR string:

>>> assignment_end.to_ocr()
'NY210088000000060000002000000000000000600170604170604000000000000000000000000000'

For details on the semantics of each field, please refer to Nets’ documentation. The reference directory of the netsgiro Git repo contains the file format specifications, which is a good place to start.

class netsgiro.records.TransmissionStart(service_code: Union[netsgiro.enums.ServiceCode, int, str], transmission_number: str, data_transmitter: str, data_recipient: str)[source]

TransmissionStart is the first record in every OCR file.

A file can only contain a single transmission.

Each transmission can contain any number of assignments.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 10
data_recipient
data_transmitter
service_code
transmission_number
class netsgiro.records.TransmissionEnd(service_code: Union[netsgiro.enums.ServiceCode, int, str], num_transactions, num_records, total_amount, nets_date: Union[datetime.date, str, None])[source]

TransmissionEnd is the first record in every OCR file.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 89
nets_date
num_records
num_transactions
service_code
total_amount
class netsgiro.records.AssignmentStart(service_code: Union[netsgiro.enums.ServiceCode, int, str], assignment_type: Union[netsgiro.enums.AssignmentType, int, str], assignment_number: str, assignment_account: str, agreement_id: Optional[str] = None)[source]

AssignmentStart is the first record of an assignment.

Each assignment can contain any number of transactions.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 20
agreement_id
assignment_account
assignment_number
assignment_type
service_code
class netsgiro.records.AssignmentEnd(service_code: Union[netsgiro.enums.ServiceCode, int, str], assignment_type: Union[netsgiro.enums.AssignmentType, int, str], num_transactions, num_records, total_amount: Union[None, int, str] = None, nets_date_1: Union[datetime.date, str, None] = None, nets_date_2: Union[datetime.date, str, None] = None, nets_date_3: Union[datetime.date, str, None] = None)[source]

AssignmentEnd is the last record of an assignment.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 88
assignment_type
nets_date

Nets’ processing date.

Only used for OCR Giro.

nets_date_1
nets_date_2
nets_date_3
nets_date_earliest

Earliest date from the contained transactions.

nets_date_latest

Latest date from the contained transactions.

num_records
num_transactions
service_code
total_amount
class netsgiro.records.TransactionAmountItem1(service_code: Union[netsgiro.enums.ServiceCode, int, str], transaction_type: Union[netsgiro.enums.TransactionType, int, str], transaction_number, nets_date: Union[datetime.date, str], amount, kid: Optional[str], centre_id: Optional[str] = None, day_code: Union[None, int, str] = None, partial_settlement_number: Union[None, int, str] = None, partial_settlement_serial_number: Optional[str] = None, sign: Optional[str] = None)[source]

TransactionAmountItem1 is the first record of a transaction.

The record is used both for AvtaleGiro and for OCR Giro.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 30
amount
centre_id
day_code
kid
nets_date
partial_settlement_number
partial_settlement_serial_number
service_code
sign
transaction_number
transaction_type
class netsgiro.records.TransactionAmountItem2(service_code: Union[netsgiro.enums.ServiceCode, int, str], transaction_type: Union[netsgiro.enums.TransactionType, int, str], transaction_number, reference: Optional[str], form_number: Optional[str] = None, bank_date: Union[datetime.date, str, None] = None, debit_account: Optional[str] = None, filler: Optional[str] = None, payer_name: Optional[str] = None)[source]

TransactionAmountItem2 is the second record of a transaction.

The record is used both for AvtaleGiro and for OCR Giro.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 31
bank_date
debit_account
form_number
payer_name
reference
service_code
transaction_number
transaction_type
class netsgiro.records.TransactionAmountItem3(service_code: Union[netsgiro.enums.ServiceCode, int, str], transaction_type: Union[netsgiro.enums.TransactionType, int, str], transaction_number, text: Optional[str])[source]

TransactionAmountItem3 is the third record of a transaction.

The record is only used for some OCR Giro transaction types.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 32
service_code
text
transaction_number
transaction_type
class netsgiro.records.TransactionSpecification(service_code: Union[netsgiro.enums.ServiceCode, int, str], transaction_type: Union[netsgiro.enums.TransactionType, int, str], transaction_number, line_number, column_number, text)[source]

TransactionSpecification is used for AvtaleGiro transactions.

The record is only used when bank notification is used to notify the payer.

Each record contains half of an 80 char long line of text and can be repeated up to 84 times for a single transaction for a total of 42 lines of specification text.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

classmethod from_text(*, service_code: netsgiro.enums.ServiceCode, transaction_type: TransactionType, transaction_number: int, text: Optional[str]) → Iterable[TransactionSpecification][source]

Create a sequence of specification records from a text string.

to_ocr() → str[source]

Get record as OCR string.

classmethod to_text(records: List[TransactionSpecification]) → str[source]

Get a text string from a sequence of specification records.

RECORD_TYPE = 49
column_number
line_number
service_code
text
transaction_number
transaction_type
class netsgiro.records.AvtaleGiroAgreement(service_code: Union[netsgiro.enums.ServiceCode, int, str], transaction_type: Union[netsgiro.enums.TransactionType, int, str], transaction_number, registration_type: Union[netsgiro.enums.AvtaleGiroRegistrationType, int, str], kid: Optional[str], notify: Union[bool, str])[source]

AvtaleGiroAgreement is used by Nets to notify about agreement changes.

This includes new or deleted agreements, as well as updates to the payer’s notification preferences.

classmethod from_string(line: str) → R

Parse OCR string into a record object.

to_ocr() → str[source]

Get record as OCR string.

RECORD_TYPE = 70
kid
notify
registration_type
service_code
transaction_number
transaction_type

Constants

netsgiro.NETS_ID

Nets’ data transmitter/recipient ID

>>> import netsgiro
>>> netsgiro.NETS_ID
'00008080'
class netsgiro.ServiceCode[source]

Service codes tell which Nets service the record applies to.

NONE = 0

Used for the transmission start and end record.

OCR_GIRO = 9

Used for all OCR Giro records.

AVTALEGIRO = 21

Used for all AvtaleGiro records.

class netsgiro.RecordType[source]

Record types tell what type of record this is.

TRANSMISSION_START = 10

See netsgiro.TransmissionStart.

ASSIGNMENT_START = 20

See netsgiro.AssignmentStart.

TRANSACTION_AMOUNT_ITEM_1 = 30

See netsgiro.TransactionAmountItem1.

TRANSACTION_AMOUNT_ITEM_2 = 31

See netsgiro.TransactionAmountItem2.

TRANSACTION_AMOUNT_ITEM_3 = 32

See netsgiro.TransactionAmountItem3.

TRANSACTION_SPECIFICATION = 49

See netsgiro.TransactionSpecification.

TRANSACTION_AGREEMENTS = 70

See netsgiro.TransactionAgreements.

ASSIGNMENT_END = 88

See netsgiro.AssignmentEnd.

TRANSMISSION_END = 89

See netsgiro.TransmissionEnd.

class netsgiro.AssignmentType[source]

Assignment types tell what type of assignment this is.

TRANSACTIONS = 0

Used both for AvtaleGiro payment requests and OCR Giro transactions.

AVTALEGIRO_AGREEMENTS = 24

Used for AvtaleGiro agreement updates.

AVTALEGIRO_CANCELLATIONS = 36

Used for AvtaleGiro cancellations.

class netsgiro.TransactionType[source]

Assignment types tell what type of transaction this is.

FROM_GIRO_DEBITED_ACCOUNT = 10

Used for OCR Giro.

FROM_STANDING_ORDERS = 11

Used for OCR Giro.

FROM_DIRECT_REMITTANCE = 12

Used for OCR Giro.

FROM_BUSINESS_TERMINAL_GIRO = 13

Used for OCR Giro.

FROM_COUNTER_GIRO = 14

Used for OCR Giro.

FROM_AVTALEGIRO = 15

Used for OCR Giro.

FROM_TELEGIRO = 16

Used for OCR Giro.

FROM_CASH_GIRO = 17

Used for OCR Giro.

REVERSING_WITH_KID = 18

Used for OCR Giro.

PURCHASE_WITH_KID = 19

Used for OCR Giro.

REVERSING_WITH_TEXT = 20

Used for OCR Giro.

PURCHASE_WITH_TEXT = 21

Used for OCR Giro.

Note

The value 21 is used for both PURCHASE_WITH_TEXT and AVTALEGIRO_WITH_BANK_NOTIFICATION. The enum representation will be <PURCHASE_WITH_TEXT: 21> in either case.

AVTALEGIRO_WITH_PAYEE_NOTIFICATION = 2

Used for AvtaleGiro when you want to notify the payer yourself.

AVTALEGIRO_WITH_BANK_NOTIFICATION = 21

Used for AvtaleGiro when you want the bank to notify the payer.

AVTALEGIRO_CANCELLATION = 93

Used for transactions that are part of an AvtaleGiro cancellation assignment.

AVTALEGIRO_AGREEMENT = 94

Used by Nets for updates to AvtaleGiro agreeements.

class netsgiro.AvtaleGiroRegistrationType[source]

AvtaleGiro registration types tell what kind of update this is.

ACTIVE_AGREEMENT = 0

Used when the AvtaleGiro agreement assignment contains all currently active agreements.

NEW_OR_UPDATED_AGREEMENT = 1

Used when the AvtaleGiro agreement assignment contains only changes, and the current agreement is new or updated.

DELETED_AGREEMENT = 2

Used when the AvtaleGiro agreement assignment contains only changes, and the current agreement has been deleted.

Changelog

v2.0.0 (2022-05-03)

Breaking

  • Dropped support for Python 3.5 and 3.6 (both have reached EOL).

New

  • Add support for Python 3.9, 3.10, and added 3.11 pre-releases to test-suite.
  • Fully annotated the project and added a py.typed to make the package PEP 561-compliant.
  • Added a get_minimum_due_date utility function for retrieving the earliest valid due date for a payment request.
  • Added a validate_due_date kwarg to the add_payment_request Assignment method, to optionally perform due date validation. When enabled, due dates less than the minimum (4 calendar days in the future, offset by holidays) or more than the maximum (12 months in the future) will raise an exception.

Internal

v1.3.0 (2020-06-10)

  • Drop support for Python 3.4, which has reached end of life.
  • Python 3.8 is now explicitly supported. No changes was required to the library, but the test suite now runs on Python 3.4, 3.5, 3.6, 3.7 and 3.8.

v1.2.0 (2018-11-22)

  • Python 3.7 is now explicitly supported. No changes was required to the library, but the test suite now runs on Python 3.4, 3.5, 3.6, and 3.7.
  • Require attrs >= 17.4, and replace all use of the deprecated keyword argument convert with converter so that netsgiro can continue working with attrs relases after January 2019.
  • Format code with Black.

v1.1.0 (2017-10-23)

Record parsing and writing are now tested with an additional suite of property based tests, using the Hypothesis library. This testing effort identified a number of issues, all of which are fixed by this release.

v1.0.0 (2017-05-20)

No changes from v1.0.0a3, which has been used in production for a few weeks without any issues.

v1.0.0a3 (2017-05-03)

v1.0.0a2 (2017-04-26)

Major improvements and changes.

  • The objects API now supports parsing all known file variants with netsgiro.parse() and can recreate the parsed OCR data perfectly with netsgiro.Transmission.to_ocr().
  • The objects API now does all the bookkeeping necessary for building payment requests. With this improvement, the code necessary to produce a payment request is cut to from around 100 to 25 lines of code.
  • New quickstart guide shows how to parse files and build payment requests.
  • All public methods and fields of both the objects API and records API are now documented.
  • The low-level records API has been moved to the netsgiro.records module.

v1.0.0a1 (2017-04-17)

Initial alpha release. No promises about backwards compatibility.