Project

saft

0.0
No release in over a year
SAF-T parser and writer
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

~> 1.13
~> 2.5
 Project Readme

SAF-T (Standard Audit File for Tax)

SAF-T is developed by OECD (Organisation for Economic Co-operation and Development). v1.0 was shipped in 2005, v2.0 was shipped in 2010.

https://en.wikipedia.org/wiki/SAF-T

Skatteetaten = The norwegian tax administration

In Norway we use version v2.0 of the format, but Skatteetaten in Norway has make some modifications and released their own versioning starting on v1.0, but that was a continuation on v2.0. I believe the only changes are related to which nodes should be used for what, so 100% backward compatible for SAF-T v2.0.

This gem is developed to work in Norway but it should be possible to make it work for all SAF-T versions. We didn't implement all Nodes but it should be quite easy to add.

Tested and verified for: None yet, Norway is in development

Even if you are able to create a AuditFile instance it doesn't mean it is valid per the xsd. There are some nodes where you have to choose this set of nodes or this other set of nodes. It would be possible to create such a structure with dry-stuct as well but we would end up with a lot more Nodes, also a bigger difference on xml structure and structs.

Setup

# if you use SAF-T to write xmls you probably want the files to be as compact as
# possible in production but nicely formatted in development end test.
# default value is AS_XML

if production
  SAFT.nokogiri_save_setting = Nokogiri::XML::Node::SaveOptions::AS_XML
else
  SAFT.nokogiri_save_setting = Nokogiri::XML::Node::SaveOptions::DEFAULT_XML
end

Usage

require "saft"

# read a SAF-T file

xml = File.read("saft.xml")
audit_file = SAFT::V2.parse(xml) # instance of SAFT::V2::Types::AuditFile or raises type errors
audit_file.header.company.name #  name from xml

# create a SAF-T file
audit_file = SAFT::V2::Types::AuditFile.call({
  header: {
    audit_file_version: "1.10",
    audit_file_country: "NO",
    audit_file_date_created: Date.today,
    # ...
  }
})

xml_content = SAFT::V2.scribe(audit_file)
# it is recommended to validate against the xsd before taking it further because 
# it is possible to create invalid xml
validations = SAFT::V2.validate(xml_content)
validations.valid? # true || false
validations.errors? # [] # array of Nokogiri::XML::SyntaxError from xsd errors

We supply three types of AuditFile which all can be used to generate the xml. Relaxed, Strict and Sliced. Relaxed does not have any constrains on text length so you are more likely to have XSD errors this way. Strict has more constraints in place, it would raise Dry::Struct::Error when called with invalid options. And the last mode, Sliced. It would cut the string at max length available.

Norway

Main site for skatteetaten https://www.skatteetaten.no/en/business-and-organisation/start-and-run/best-practices-accounting-and-cash-register-systems/saf-t-financial/documentation/

Git repo from skatteetaten https://github.com/Skatteetaten/saf-t

SAF-T contains two formats, SAF-T Financial and SAF-T Cash Register.

Every electronic ERP system has to implement SAF-T Financial. SAF-T Cash Register is either for the cash register to implement or the ERP, I don't know yet. (I believe Cash Register since there should be event log which include open drawer, copy receipt, etc)

Html

We also ship a render for html view. It also ships styles

audit_file = SAFT::V2::Types::AuditFile.call({})
html = SAFT::V2.to_html(audit_file)
css = SAFT::V2::HTML.css
css_path = SAFT::V2::HTML.css_path

Dev to rebuild styles

pnpm tailwindcss -i lib/saft/v2/html.css -o ./lib/saft/v2/html_dist.css