447 lines
11 KiB
Perl
447 lines
11 KiB
Perl
package Owlcode::Tech::Facturer::MainCommand;
|
|
|
|
use v5.36.0;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use utf8;
|
|
|
|
use feature 'signatures';
|
|
|
|
use Data::Dumper;
|
|
|
|
use Moo;
|
|
use DateTime;
|
|
use Mojo::Template;
|
|
use Path::Tiny;
|
|
|
|
use Owlcode::Tech::Facturer;
|
|
|
|
my $SIMPLIFICADA = 1;
|
|
my $COMPLETA = 2;
|
|
|
|
my $IVA_INCLUDED = 1;
|
|
my $IVA_NOT_INCLUDED = 2;
|
|
|
|
binmode STDOUT, ':utf8';
|
|
binmode STDIN, ':utf8';
|
|
|
|
sub run ($self) {
|
|
my $app = Owlcode::Tech::Facturer->new;
|
|
|
|
my $self_name = $app->fullname;
|
|
my $self_dni = $app->dni;
|
|
my $self_address = $app->address;
|
|
my $self_postal_code = $app->postal_code;
|
|
my $logo = $app->image;
|
|
my $client_dni;
|
|
my $client_name;
|
|
my $client_postal_code;
|
|
my $client_address;
|
|
|
|
say "Tu nombre completo es $self_name.";
|
|
say "Tu dni es $self_dni.";
|
|
say "Tu dirección es $self_address.";
|
|
say "Tu código postal es $self_postal_code.";
|
|
|
|
my $number = $self->ask_bill_number;
|
|
say "Creando factura $number.";
|
|
my $date = $self->ask_for_date;
|
|
say "Escogida fecha: @{[$self->print_date($date)]}.";
|
|
my $kind_of_bill = $self->ask_kind_of_bill;
|
|
say $kind_of_bill;
|
|
if ( $kind_of_bill eq $COMPLETA ) {
|
|
$client_dni = $self->ask_for_client_dni;
|
|
$client_name = $self->ask_for_client_name;
|
|
$client_postal_code = $self->ask_for_client_postal_code;
|
|
$client_address = $self->ask_for_client_address;
|
|
}
|
|
my $concepts = $self->ask_for_concepts;
|
|
print 'Los conceptos son: ' . Data::Dumper::Dumper $concepts;
|
|
|
|
my $final_data = $self->final_data($concepts);
|
|
my $mt = Mojo::Template->new( auto_escape => 1 );
|
|
require MIME::Base64;
|
|
path("FACTURA @{[sprintf '%06d', $number]}.html")->spew_utf8(
|
|
$mt->vars(1)->render(
|
|
$self->template_bill,
|
|
{
|
|
self_name => $self_name,
|
|
self_dni => $self_dni,
|
|
self_address => $self_address,
|
|
self_postal_code => $self_postal_code,
|
|
client_dni => $client_dni,
|
|
client_name => $client_name,
|
|
client_address => $client_address,
|
|
client_postal_code => $client_postal_code,
|
|
number => $number,
|
|
date => $self->print_date($date),
|
|
kind_of_bill => $kind_of_bill,
|
|
SIMPLIFICADA => $SIMPLIFICADA,
|
|
COMPLETA => $COMPLETA,
|
|
concepts => $concepts,
|
|
final_data => $final_data,
|
|
logo => MIME::Base64::encode_base64($logo->slurp_raw),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
sub template_bill ($self) {
|
|
my $text = <<'EOF';
|
|
<html>
|
|
<head>
|
|
</head>
|
|
<body>
|
|
<div class="page-contents">
|
|
<img class="logo" src="data:image/svg+xml;base64,<%=$logo%>"/>
|
|
<h1>Factura <%=$kind_of_bill eq $SIMPLIFICADA ? 'Simplificada' : 'Completa'%></h1>
|
|
|
|
<h2>Datos de factura</h2>
|
|
|
|
<table>
|
|
<tr>
|
|
<th>Fecha</th>
|
|
<td><%=$date%></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Número</th>
|
|
<td><%=$number%></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2>Datos de emisión recepción.</h2>
|
|
|
|
<table>
|
|
<tr>
|
|
<th></th>
|
|
<th>Nombre Completo</th>
|
|
<th>DNI/CIF/NIE</th>
|
|
<th>Código postal</th>
|
|
<th>Dirección</th>
|
|
</tr>
|
|
<tr>
|
|
<td>Emisor</td>
|
|
<td><%= $self_name %></td>
|
|
<td><%= $self_dni %></td>
|
|
<td><%= $self_postal_code %></td>
|
|
<td><%= $self_address %></td>
|
|
</tr>
|
|
% if ($kind_of_bill eq $COMPLETA) {
|
|
<tr>
|
|
<td>Receptor</td>
|
|
<td><%= $client_name %></td>
|
|
<td><%= $client_dni %></td>
|
|
<td><%= $client_postal_code %></td>
|
|
<td><%= $client_address %></td>
|
|
</tr>
|
|
% } else {
|
|
<tr>
|
|
<td>Receptor</td>
|
|
<td>Clientes varios</td>
|
|
<td>---</td>
|
|
<td>---</td>
|
|
<td>---</td>
|
|
</tr>
|
|
% }
|
|
</table>
|
|
|
|
<h2>Desglose factura.</h2>
|
|
|
|
<table>
|
|
<tr>
|
|
<th>Concepto</th>
|
|
<th>Base</th>
|
|
<th>Total</th>
|
|
</tr>
|
|
% for my $concept (@$concepts) {
|
|
<tr>
|
|
<td><%=$concept->{title}%></td>
|
|
<td><%=sprintf "%.02f", $concept->{base}%>€</td>
|
|
<td><%=sprintf "%.02f", $concept->{total}%>€</td>
|
|
</tr>
|
|
% }
|
|
</table>
|
|
<h2>Precio Final.</h2>
|
|
|
|
<table>
|
|
<tr>
|
|
<th>Base.</th>
|
|
<th>%IVA.</th>
|
|
<th>Cuota IVA.</th>
|
|
<th>Total.</th>
|
|
</tr>
|
|
<tr>
|
|
<td><%=$final_data->{base}%>€</td>
|
|
<td>21%</td>
|
|
<td><%=$final_data->{iva}%>€</td>
|
|
<td><%=$final_data->{total}%>€</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<style>
|
|
img.logo {
|
|
width: 40px;
|
|
aspect-ratio: 1 / 1;
|
|
}
|
|
div.page-contents {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: start;
|
|
}
|
|
h1 {
|
|
align-self: center;
|
|
}
|
|
td,th {
|
|
border: solid 1px black;
|
|
}
|
|
</style>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
return $text;
|
|
}
|
|
|
|
sub final_data ( $self, $concepts ) {
|
|
my $final_total = 0;
|
|
my $final_base = 0;
|
|
my $final_iva = 0;
|
|
for my $concept (@$concepts) {
|
|
my $base = $concept->{base};
|
|
my $total = $concept->{total};
|
|
$final_total += $total;
|
|
$final_base += $base;
|
|
}
|
|
$final_base = sprintf( "%.02f", $final_base );
|
|
$final_total = sprintf( "%.02f", $final_total );
|
|
$final_iva = $final_total - $final_base;
|
|
return {
|
|
iva => $final_iva,
|
|
total => $final_total,
|
|
base => $final_base,
|
|
};
|
|
}
|
|
|
|
sub ask_for_client_postal_code ($self) {
|
|
my $input;
|
|
while ( !defined $input || $input =~ /^\s*$/ ) {
|
|
print "Introduce el código postal del cliente o su empresa
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
sub ask_for_client_address ($self) {
|
|
my $input;
|
|
while ( !defined $input || $input =~ /^\s*$/ ) {
|
|
print "Introduce la dirección (calle y número) del cliente o su empresa
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
sub ask_for_client_name ($self) {
|
|
my $input;
|
|
while ( !defined $input || $input =~ /^\s*$/ ) {
|
|
print "Introduce el nombre completo del cliente o su empresa
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
sub ask_for_client_dni ($self) {
|
|
my $input;
|
|
while ( !defined $input || $input =~ /^\s*$/ ) {
|
|
print "Introduce DNI/CIF/NIE. (No se validará, ten cuidado.)
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
sub ask_one_concept ($self) {
|
|
my $title = $self->ask_concept_title;
|
|
my $total = $self->ask_concept_total;
|
|
my $base = $total / 1.21;
|
|
return {
|
|
title => $title,
|
|
total => $total,
|
|
base => $base,
|
|
};
|
|
}
|
|
|
|
sub ask_concept_total ($self) {
|
|
my $has_iva = $self->ask_concept_has_iva_included;
|
|
my $input;
|
|
while ( !defined $input || $input !~ /^[0-9]+(?:\.[0-9]+)?$/ ) {
|
|
|
|
print "Introduce precio " . ( $has_iva ? 'con' : 'sin' ) . " IVA:
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
if ($has_iva) {
|
|
return $input;
|
|
}
|
|
return $input * 1.21;
|
|
}
|
|
|
|
sub ask_concept_has_iva_included ($self) {
|
|
my $input;
|
|
while ( !defined $input
|
|
|| $input ne $IVA_INCLUDED
|
|
&& $input ne $IVA_NOT_INCLUDED
|
|
&& $input ne '' )
|
|
{
|
|
print "Escoge modalidad de IVA en el precio que vas a introducir:
|
|
($IVA_INCLUDED) IVA incluido.
|
|
($IVA_NOT_INCLUDED) IVA NO incluido.
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
if ( $input eq $IVA_INCLUDED ) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub ask_concept_title ($self) {
|
|
my $input;
|
|
if ( !defined $input || $input =~ /^\s*$/ ) {
|
|
print "Introduce el concepto de la factura:
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
sub ask_for_concepts ($self) {
|
|
my @concepts;
|
|
push @concepts, $self->ask_one_concept;
|
|
while ( $self->ask_do_want_more_concepts ) {
|
|
push @concepts, $self->ask_one_concept;
|
|
}
|
|
return [@concepts];
|
|
}
|
|
|
|
sub ask_do_want_more_concepts ($self) {
|
|
my $input;
|
|
while ( !defined $input
|
|
|| $input ne '' && lc($input) ne 'y' && lc($input) ne 'n' )
|
|
{
|
|
print "¿Quieres introducir más conceptos? [n]
|
|
> ";
|
|
$input = <STDIN>;
|
|
chomp $input;
|
|
}
|
|
if ( lc($input) eq 'y' ) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub ask_for_date ($self) {
|
|
my $date = DateTime->now;
|
|
my $user_response;
|
|
|
|
while ( !defined $user_response
|
|
|| $user_response ne ''
|
|
&& lc($user_response) ne 'y'
|
|
&& lc($user_response) ne 'n' )
|
|
{
|
|
print "¿Quieres continuar con la fecha actual? "
|
|
. " @{[$self->print_date($date)]} [Y]
|
|
> ";
|
|
$user_response = <STDIN>;
|
|
chomp $user_response;
|
|
}
|
|
if ( $user_response eq '' || lc($user_response) eq 'y' ) {
|
|
return $date;
|
|
}
|
|
$self->ask_year($date);
|
|
$self->ask_month($date);
|
|
$self->ask_day($date);
|
|
}
|
|
|
|
sub ask_day ( $self, $date ) {
|
|
my $day;
|
|
while ( !defined $day || $day ne '' && $day !~ /^[0-9]+$/ ) {
|
|
print "Escoge un día. [@{[$date->day]}]
|
|
> ";
|
|
$day = <STDIN>;
|
|
chomp $day;
|
|
}
|
|
if ( $day eq '' ) {
|
|
return;
|
|
}
|
|
$date->set_day($day);
|
|
}
|
|
|
|
sub ask_month ( $self, $date ) {
|
|
my $month;
|
|
while ( !defined $month || $month ne '' && $month !~ /^[0-9]+$/ ) {
|
|
print "Escoge un mes. [@{[$date->month]}]
|
|
> ";
|
|
$month = <STDIN>;
|
|
chomp $month;
|
|
}
|
|
if ( $month eq '' ) {
|
|
return;
|
|
}
|
|
$date->set_month($month);
|
|
}
|
|
|
|
sub ask_year ( $self, $date ) {
|
|
my $year;
|
|
while ( !defined $year || $year ne '' && $year !~ /^[0-9]+$/ ) {
|
|
print "Escoge un año. [@{[$date->year]}]
|
|
> ";
|
|
$year = <STDIN>;
|
|
chomp $year;
|
|
}
|
|
if ( $year eq '' ) {
|
|
return;
|
|
}
|
|
$date->set_year($year);
|
|
}
|
|
|
|
sub print_date ( $self, $date ) {
|
|
return $date->strftime('%F');
|
|
}
|
|
|
|
sub ask_kind_of_bill ($self) {
|
|
my $kind_of_bill;
|
|
while ( !defined $kind_of_bill
|
|
|| $kind_of_bill ne $SIMPLIFICADA && $kind_of_bill ne $COMPLETA )
|
|
{
|
|
print "¿Qué tipo de factura quieres crear?
|
|
($SIMPLIFICADA) Simplificada.
|
|
($COMPLETA) Completa.
|
|
> ";
|
|
$kind_of_bill = <STDIN>;
|
|
chomp $kind_of_bill;
|
|
}
|
|
return $kind_of_bill;
|
|
}
|
|
|
|
sub ask_bill_number ($self) {
|
|
my $number;
|
|
while ( !defined $number || $number !~ /^[0-9]+$/ ) {
|
|
print "¿Cual es el número de factura?
|
|
> ";
|
|
$number = <STDIN>;
|
|
chomp $number;
|
|
}
|
|
return $number;
|
|
}
|
|
1;
|