The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

=encoding utf8
=head1 NAME
Business::CAMT - ISO20022 Cash Management (CAMT) messages
=head1 SYNOPSIS
my $camt = Business::CAMT->new;
my $msg = $camt->read($file|$xml);
print $msg->toPerl;
print $msg->toJSON;
my $message = $camt->create('053.001.02', $data);
$camt->write('out.xml', $message);
$message->write('out.xml'); # same
=head1 DESCRIPTION
Use this module to manage CAMT messages, which are ISO20022 standard
"Cash Management" messages as produced in banking. For instance,
CAMT.053 is produced by banks and consumed by accountancies, showing
transactions in bank-accounts. See F<https://www.iso20022.org>.
At the moment, this module can be used to read and write the XML
message files, perfectly validated and predictable. It is intended to
also support abstraction in interpreting and constructing the content.
However, B<I need a sponsor> to make that happen. Contact the author
for support. Please. I would also like to include a CAMT.053 to CSV
and MT-940 converter, v.v. Please hire me.
=head1 METHODS
=head2 Constructors
=over 4
=item Business::CAMT-E<gt>B<new>(%options)
Create a new CAMT processing object.
Reuse this object to avoid recompilation of the message processor, which
is pretty expensive.
-Option --Default
big_numbers false
long_tagnames false
match_schema 'NEWER'
=over 2
=item big_numbers => BOOLEAN
Set to a true value when your accounts run into the billions. This will
enable Math::BigFloat to be used, which is slower and memory hungry.
=item long_tagnames => BOOLEAN
The schemas are derived from an UML specifications which uses clear and
readible long names for relations and attributes. But, someone with a
poor sense of optimization removed most of the vowels from these tags
while translating the UML into an XSD. When set to C<true>, this option
will give you the nice long names in Perl.
=item match_schema => $rule
Sets the default $rule how to handle messages which have namespaces
which do not match the schemas contained in the module release.
See L<matchSchema()|Business::CAMT/"Helper methods"> for the available $rule values. See the L<DETAILS>
section about the namespace versioning horrors.
=back
=back
=head2 Accessors
=over 4
=item $obj-E<gt>B<schemas>()
Returns the XML::Compile::Schema object, which collects the
compiled message XSDs. The XSDs get automatically loaded when
messages are encountered which need them.
=back
=head2 Read and Write messages
=over 4
=item $obj-E<gt>B<create>($type, $data, %options)
Create a new message, to be written later. The C<$data> is the content of
the message, in a structure as can be found in the example templates.
The C<$type> is either in the form C<camt.053.001.02> or C<053.001.02>.
example: of create
my $camt = Business::CAMT->new(...);
my $msg = $camt->create('053.001.02', \%data);
$msg->write('file.xml');
=item $obj-E<gt>B<fromHASH>(\%data, %options)
Create a L<Business::CAMT::Message|Business::CAMT::Message> object of the given C<type>. It is not
checked whether the type schema exists until an attempt is made to write the
message.
-Option--Default
type <required>
=over 2
=item type => VERSION
Something like C<camt.053.001.02> or C<053.001.02>.
=back
=item $obj-E<gt>B<read>($file|$xml, %options)
Pass a $file name, an $xml document or an $xml element. Returned is
a HASH blessed in class 'Business::CAMT::CAMT<nr>', for instance
C<Business::CAMT::CAMT053>.
-Option --Default
match_schema new(match_schema)
=over 2
=item match_schema => $rule
=back
=item $obj-E<gt>B<write>($file, $message, %options)
Write a constructed C<$message> (an extension of L<Business::CAMT::Message|Business::CAMT::Message>)
to a file in XML format. The message can also be written as JSON or Perl
data-structure, via the message itself.
example: writing a message
my $message = $camt->create('053.001.02', $data);
$camt->write('out.xml', $message);
$message->write('out.xml'); # same
=back
=head2 Helper methods
You would rarely (or never) need to use these methods in your programs: they support
the reader and writer function.
=over 4
=item $obj-E<gt>B<fullname2tagTable>()
Translates long and understandable names into (silly) abbreviated tags.
=item $obj-E<gt>B<knownVersions>( [$set] )
Returns a sorted LIST with all schema versions which are included in this
distribution. When the $set is specified (like C<053.001>), then only
those are reported.
=item $obj-E<gt>B<matchSchema>($set, $version, %options)
Find the available schema version for the $set (like '053.001') to interpret
a message with $version (like '02').
-Option--Default
rule new(match_schema)
=over 2
=item rule => 'EXACT'|'NEWER'|'NEWEST'|'ANY'|CODE
When C<EXACT>, only the precise version is acceptable. C<NEWER> will
fall back to the closest newer version. When no exact match, C<NEWEST>
returns the highest available version, but must be newer. Most generous
is C<ANY>, which falls back to the newest available version even when
it is older than the message version.
You may also pass a CODE reference, which is called with the $set, the requested
schema, and a sorted ARRAY of available versions. It must return one of the
available versions or C<undef> (no compatible version).
=back
=item $obj-E<gt>B<schemaReader>($set, $version, $ns)
Produces a CODE which can be called with an XML message, to get it transformed
into a Perl data-structure. In this case, the $set and $version have to be
known already; method L<read()|Business::CAMT/"Read and Write messages"> figures that out by itself.
=item $obj-E<gt>B<schemaWriter>($set, $version, $ns)
=item $obj-E<gt>B<tag2fullnameTable>()
Returns a table which translates the (silly) abbreviations used in the
XML tags into readable names. Good names make it easier to understand
the handling code and is less error-prone.
=back
=head1 DETAILS
In this chapter, you find some background information and implementation tips.
=head2 Examples
The release contains a C<examples/> directory. In that directory, you with find
a C<show> script and some xml files. Run the script with a file, to see what
this module has to offer. For example:
cd Business-CAMT-0.01/
examples/show examples/danskeci.com/camt053_dk_example.xml /tmp/a.xml
The script (this module) auto-detects the CAMT type which is found in the XML
message. Play with the script to see how changes affect the output.
=head2 Templates
In our GitHub repository, you find a C<templates/> directory which
contains a structural dump of each of the Perl data structure which is
produced (for L<read()|Business::CAMT/"Read and Write messages">) or consumed (for C<write>, to be implemented)
by this module.
Be sure you understand anonymous HASHes and ARRAYs in Perl well, when you
start playing. Do not forget that code gets more readible when you use
practical reference variables.
On GitHub, you will also find a C<templates-long/> directory full of
examples. This demonstrates what option L<new(long_tagnames)|Business::CAMT/"Constructors"> does: it
will make the Perl datastructures readible.
=head2 Implementation issues
=head3 XML namespaces
The idea behind XML namespaces to include schema versioning is
fundamentally flawed. The CAMT files form no exception in this broken
XML concept. Of course, schema versions are mainly backwards compatible,
so why not design your versioning with that in mind?
This module bends the namespace use to hide these design mistakes
into flexible code. Without full knowledge about existing and future
versions of the schemas, there is a powerfull configuration setting
with L<matchSchema()|Business::CAMT/"Helper methods">.
Please consider to contribute discovered incompatibility issues to this
module, to hide them where possible.
=head3 Tag abbreviations
XML is very verbose anyway, so it really does not help to abbreviate tags
leaving some vowels out. This makes it harder to read messages and code.
It increases the chance on stupid typos in the code.
When you set L<new(long_tagnames)|Business::CAMT/"Constructors">, then your Perl structure will use
longer, understandable names: it gets easy to understand the message
without reading the documentation. This improves maintenance on the
long run.
This option will be applied both on L<read()|Business::CAMT/"Read and Write messages"> and L<write()|Business::CAMT/"Read and Write messages">. Of course,
the templates will show you how it works: see the C<templates-long/>
directory in the github repository.
=head3 No common types
Each schema is separate, although their type definitions are overlapping.
It is not guaranteed that equal types will stay that way over time.
This may cause instable code.
Probably, these issues will not emerge because the schema files are
generated from a central UML model. However: small changes in the data
structure will cause multiple schemas to change to a new version.
A better setup would be:
=over 4
=item * a schema for base types, like "Amount"
=item * a schema for more complex (reused) structures
=item * a schema per message, which composes the complex structures
=back
=head3 Missed chances on XML
The way these schema's got generated, make them very low in using more
powerful XML schema features. Those features would have helped the
stability of the "interface" which these messages implement a lot.
Done this way, XML is not much better than JSON. To be honest, the
schemas are littered by missed chances.
The messages are designed with an UML tool, which means: limited to the
features of that tool and hindering the view on the quality of the schema.
This leads to structures like:
<Bal>
<Tp>
<CdOrPrtry>
<Cd>OPBD</Cd>
</CdOrPrtry>
</Tp>
<Amt Ccy="SEK">500000</Amt>
<CdtDbtInd>CRDT</CdtDbtInd>
<Dt>
<Dt>2010-10-15</Dt>
</Dt>
</Bal>
In Perl, this leads to (C<long_tagnames> on)
Balance => {
Type => {
CodeOrProperty => {
Code => 'OPBD',
}
},
Amount => {
_ => '500000',
Currency => 'SEK',
},
CreditDebitInd => 'CRDT',
Date => {
Date => '2010-10-15',
},
}
The XML schema, when B<designed> as XML schema, could have looked like
<OpeningBook Date="2010-10-15CEST">
<Credit Currency="SEK">500000.00</Credit>
</OpeningBook>
The use of C<group>'ed elements and C<substitutionGroup>'s would have
made messages so much clearer and easier. Even simple constructs
as C<extension> and C<restriction> of C<complexType>'s are not used.
It would have reduced the message size much further than by leaving
out the vowels from tags, as the example shows.
But more importantly: this hinders backward compatibility in the message
evaluation a lot! Using XML features better would result in better
maintainable applications. Much better.
=head1 SEE ALSO
This module is part of Business-CAMT distribution version 0.13,
built on December 02, 2024. Website: F<http://perl.overmeer.net/CPAN/>
=head1 LICENSE
Copyrights 2024 by [Mark Overmeer <markov@cpan.org>]. For other contributors see ChangeLog.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.