package SL::Controller::ScanQRBill;

use strict;
use parent qw(SL::Controller::Base);

use List::Util qw(first);
use URI::Escape;

use SL::Helper::QrBillParser;
use SL::DB::Vendor;
use SL::DB::Chart;
use SL::DB::Tax;
use SL::DB::ValidityToken;

use Rose::Object::MakeMethods::Generic(
  #scalar => [ qw() ],
  'scalar --get_set_init' => [ qw(vendors accounts_AP_amount accounts_AP taxcharts) ],
);

# check permissions
__PACKAGE__->run_before(sub { $::auth->assert('ap_transactions'); });

################ actions #################

sub action_scan_view {
  my ($self) = @_;

  $::request->layout->add_javascripts('html5-qrcode.js');
  $::request->layout->add_javascripts('kivi.ScanQRBill.js');

  $self->render('scan_qrbill/scan_view',
    transaction_success => $::form->{transaction_success} // '0',
    invoice_number => $::form->{invnumber} // '',
    developer => $::auth->assert('developer', 1) ? '1' : '0',
  );
}

sub action_handle_scan_result {
  my ($self) = @_;

  my $qrtext = $::form->{qrtext};

  # load text into object
  $self->{qr_obj} = SL::Helper::QrBillParser->new($qrtext);

  # check if valid qr-bill
  if (!$self->{qr_obj}->is_valid) {
    return $self->js
      ->run('kivi.ScanQRBill.popupInvalidQRBill', $self->{qr_obj}->error)
      ->render();
  }

  my $vendor_name = $self->{qr_obj}->{creditor}->{name};
  $self->{vendor} = first { $_->{name} eq $vendor_name } @{ $self->vendors };

  if (!$self->{vendor}) {
    return $self->js
      ->run('kivi.ScanQRBill.popupVendorNotFound', $vendor_name)
      ->render();
  }

  $self->prepare_add_purchase_transaction();
}

################# internal ###############

sub prepare_add_purchase_transaction {
  my ($self) = @_;

  my $qr_obj = $self->{qr_obj};

  my $token = SL::DB::ValidityToken->create(scope => SL::DB::ValidityToken::SCOPE_PURCHASE_INVOICE_POST())->token;

  my $html = $self->render('scan_qrbill/_add_purchase_transaction',
    { output => 0 },
    vendor => {
      name => $self->{vendor}->{name},
      number => $self->{vendor}->{vendornumber},
      id => $self->{vendor}->{id},
    },
    qrbill => {
      unstructured_message => $qr_obj->{additional_information}->{unstructured_message},
      reference_type => $qr_obj->{payment_reference}->{reference_type},
      reference => $qr_obj->{payment_reference}->{reference},
      amount => $qr_obj->{payment_amount_information}->{amount},
      currency => $qr_obj->{payment_amount_information}->{currency},
      data_encoded => uri_escape($qr_obj->raw_data),
    },
    accounts_AP_amount => $self->accounts_AP_amount,
    accounts_AP => $self->accounts_AP,
    taxcharts => $self->taxcharts,
    form_validity_token => $token,
  );

  $self->js->html('#main-content', $html)->render();
}

sub init_vendors {
  SL::DB::Manager::Vendor->get_all();
}

sub init_accounts_AP_amount {
  [ map { {
      text => "$_->{accno} - $_->{description}",
      accno => $_->{accno},
      id => $_->{id},
      chart_id => $_->{id},
    } } @{ SL::DB::Manager::Chart->get_all(
      query   => [ SL::DB::Manager::Chart->link_filter('AP_amount') ],
      sort_by => 'id ASC') }
  ];
}

sub init_accounts_AP {
  [ map { {
      text => "$_->{accno} - $_->{description}",
      accno => $_->{accno},
      id => $_->{id},
      chart_id => $_->{id},
    } } @{ SL::DB::Manager::Chart->get_all(
      query   => [ SL::DB::Manager::Chart->link_filter('AP') ],
      sort_by => 'id ASC') }
  ];
}

sub init_taxcharts {
  [ map { {
      text => "$_->{taxkey} - $_->{taxdescription} " . ($_->{rate} * 100) .' %',
      id => "$_->{id}--$_->{rate}",
    } } @{ SL::DB::Manager::Tax->get_all(
    where   => [ chart_categories => { like => '%E%' }],
    sort_by => 'taxkey, rate') }
  ];
}

1;

__END__

=pod

=encoding utf-8

=head1 NAME

SL::Controller::ScanQRBill - Controller for scanning swiss QR-Bills using the mobile template

=head1 DESCRIPTION

Renders the scan view in the mobile template and handles the scan result.

The scanned QR-Bill data is parsed and the vendor is searched in the database.

If everything is valid an add purchase transaction view is rendered and
the QR-Bill can be saved as a purchase transaction.

The post function from ap.pl is used to save the purchase transaction.

The raw data of the QR-Bill is stored with the purchase transaction in the ap table
in the field qrbill_data.
The data can later be accessed again using the parser module SL::Helper::QrBillParser.

=head1 SECURITY CONSIDERATIONS

In theory an attacker could try to insert a malicious Javascript code into a qr code,
that is then scanned, and redisplayed in the browser (XSS).

Therefore it is important to escape any data coming from the qr code when it is rendered
in the templates. For this we use the template toolkit html filter: [% qrdata | html %],
Jquery's text function: $('#qrdata').text(qrdata);, and URI::Escape; for the raw data.

For database insertion we use prepared statements (AP.pm).

=head1 TESTING

To simplify testing the scan view shows some buttons to send example qr codes, when in
developer mode. Sending is implemented in Javascript in js/kivi.ScanQRBill.js.

=head1 URL ACTIONS

=over 4

=item C<scan_view>

Renders the scan view in the mobile template.

=item C<handle_scan_result>

Handles the scan result and renders the add purchase transaction view.

=back

=head1 TODO

=head2 Additional features:

=over 4

=item * automatically extract invoice number and dates etc. from "SWICO-String" if present

=item * Option to add the vendor if not found

=back

=head1 BUGS

Nothing here yet.

=head1 AUTHOR

Cem Aydin E<lt>cem.aydin@revamp-it.chE<gt>

=cut
