]> wagnertech.de Git - mfinanz.git/blob - SL/Controller/ScanQRBill.pm
kivitendo 3.9.2-0.2
[mfinanz.git] / SL / Controller / ScanQRBill.pm
1 package SL::Controller::ScanQRBill;
2
3 use strict;
4 use parent qw(SL::Controller::Base);
5
6 use List::Util qw(first);
7 use URI::Escape;
8
9 use SL::Helper::QrBillParser;
10 use SL::DB::Vendor;
11 use SL::DB::Chart;
12 use SL::DB::Tax;
13 use SL::DB::ValidityToken;
14
15 use Rose::Object::MakeMethods::Generic(
16   #scalar => [ qw() ],
17   'scalar --get_set_init' => [ qw(vendors accounts_AP_amount accounts_AP taxcharts) ],
18 );
19
20 # check permissions
21 __PACKAGE__->run_before(sub { $::auth->assert('ap_transactions'); });
22
23 ################ actions #################
24
25 sub action_scan_view {
26   my ($self) = @_;
27
28   $::request->layout->add_javascripts('html5-qrcode.js');
29   $::request->layout->add_javascripts('kivi.ScanQRBill.js');
30
31   $self->render('scan_qrbill/scan_view',
32     transaction_success => $::form->{transaction_success} // '0',
33     invoice_number => $::form->{invnumber} // '',
34     developer => $::auth->assert('developer', 1) ? '1' : '0',
35   );
36 }
37
38 sub action_handle_scan_result {
39   my ($self) = @_;
40
41   my $qrtext = $::form->{qrtext};
42
43   # load text into object
44   $self->{qr_obj} = SL::Helper::QrBillParser->new($qrtext);
45
46   # check if valid qr-bill
47   if (!$self->{qr_obj}->is_valid) {
48     return $self->js
49       ->run('kivi.ScanQRBill.popupInvalidQRBill', $self->{qr_obj}->error)
50       ->render();
51   }
52
53   my $vendor_name = $self->{qr_obj}->{creditor}->{name};
54   $self->{vendor} = first { $_->{name} eq $vendor_name } @{ $self->vendors };
55
56   if (!$self->{vendor}) {
57     return $self->js
58       ->run('kivi.ScanQRBill.popupVendorNotFound', $vendor_name)
59       ->render();
60   }
61
62   $self->prepare_add_purchase_transaction();
63 }
64
65 ################# internal ###############
66
67 sub prepare_add_purchase_transaction {
68   my ($self) = @_;
69
70   my $qr_obj = $self->{qr_obj};
71
72   my $token = SL::DB::ValidityToken->create(scope => SL::DB::ValidityToken::SCOPE_PURCHASE_INVOICE_POST())->token;
73
74   my $html = $self->render('scan_qrbill/_add_purchase_transaction',
75     { output => 0 },
76     vendor => {
77       name => $self->{vendor}->{name},
78       number => $self->{vendor}->{vendornumber},
79       id => $self->{vendor}->{id},
80     },
81     qrbill => {
82       unstructured_message => $qr_obj->{additional_information}->{unstructured_message},
83       reference_type => $qr_obj->{payment_reference}->{reference_type},
84       reference => $qr_obj->{payment_reference}->{reference},
85       amount => $qr_obj->{payment_amount_information}->{amount},
86       currency => $qr_obj->{payment_amount_information}->{currency},
87       data_encoded => uri_escape($qr_obj->raw_data),
88     },
89     accounts_AP_amount => $self->accounts_AP_amount,
90     accounts_AP => $self->accounts_AP,
91     taxcharts => $self->taxcharts,
92     form_validity_token => $token,
93   );
94
95   $self->js->html('#main-content', $html)->render();
96 }
97
98 sub init_vendors {
99   SL::DB::Manager::Vendor->get_all();
100 }
101
102 sub init_accounts_AP_amount {
103   [ map { {
104       text => "$_->{accno} - $_->{description}",
105       accno => $_->{accno},
106       id => $_->{id},
107       chart_id => $_->{id},
108     } } @{ SL::DB::Manager::Chart->get_all(
109       query   => [ SL::DB::Manager::Chart->link_filter('AP_amount') ],
110       sort_by => 'accno ASC') }
111   ];
112 }
113
114 sub init_accounts_AP {
115   [ map { {
116       text => "$_->{accno} - $_->{description}",
117       accno => $_->{accno},
118       id => $_->{id},
119       chart_id => $_->{id},
120     } } @{ SL::DB::Manager::Chart->get_all(
121       query   => [ SL::DB::Manager::Chart->link_filter('AP') ],
122       sort_by => 'accno ASC') }
123   ];
124 }
125
126 sub init_taxcharts {
127   [ map { {
128       text => "$_->{taxkey} - $_->{taxdescription} " . ($_->{rate} * 100) .' %',
129       id => "$_->{id}--$_->{rate}",
130     } } @{ SL::DB::Manager::Tax->get_all(
131     where   => [ chart_categories => { like => '%E%' }],
132     sort_by => 'taxkey, rate') }
133   ];
134 }
135
136 1;
137
138 __END__
139
140 =pod
141
142 =encoding utf-8
143
144 =head1 NAME
145
146 SL::Controller::ScanQRBill - Controller for scanning swiss QR-Bills using the mobile template
147
148 =head1 DESCRIPTION
149
150 Renders the scan view in the mobile template and handles the scan result.
151
152 The scanned QR-Bill data is parsed and the vendor is searched in the database.
153
154 If everything is valid an add purchase transaction view is rendered and
155 the QR-Bill can be saved as a purchase transaction.
156
157 The post function from ap.pl is used to save the purchase transaction.
158
159 The raw data of the QR-Bill is stored with the purchase transaction in the ap table
160 in the field qrbill_data.
161 The data can later be accessed again using the parser module SL::Helper::QrBillParser.
162
163 =head1 SECURITY CONSIDERATIONS
164
165 In theory an attacker could try to insert a malicious Javascript code into a qr code,
166 that is then scanned, and redisplayed in the browser (XSS).
167
168 Therefore it is important to escape any data coming from the qr code when it is rendered
169 in the templates. For this we use the template toolkit html filter: [% qrdata | html %],
170 Jquery's text function: $('#qrdata').text(qrdata);, and URI::Escape; for the raw data.
171
172 For database insertion we use prepared statements (AP.pm).
173
174 =head1 TESTING
175
176 To simplify testing the scan view shows some buttons to send example qr codes, when in
177 developer mode. Sending is implemented in Javascript in js/kivi.ScanQRBill.js.
178
179 =head1 URL ACTIONS
180
181 =over 4
182
183 =item C<scan_view>
184
185 Renders the scan view in the mobile template.
186
187 =item C<handle_scan_result>
188
189 Handles the scan result and renders the add purchase transaction view.
190
191 =back
192
193 =head1 TODO
194
195 =head2 Additional features:
196
197 =over 4
198
199 =item * automatically extract invoice number and dates etc. from "SWICO-String" if present
200
201 =item * Option to add the vendor if not found
202
203 =back
204
205 =head1 BUGS
206
207 Nothing here yet.
208
209 =head1 AUTHOR
210
211 Cem Aydin E<lt>cem.aydin@revamp-it.chE<gt>
212
213 =cut