Adding initial project.

This commit is contained in:
Sergiotarxz 2023-09-16 01:59:24 +02:00
parent 73b06c3a98
commit 2f40bb45ea
6 changed files with 449 additions and 0 deletions

19
Build.PL Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
use Module::Build;
my $home = $ENV{HOME};
my $build = Module::Build->new(
module_name => 'Owlcode::Tech::Facturer',
license => 'AGPLv3',
dist_author => 'Sergio Iglesias <contact@owlcode.tech>',
dist_abstract => 'Make a spanish facture.',
requires => {
'Mojolicious' => 0,
'Moo' => 0,
'Perl::Tidy' => 0,
'Path::Tiny' => 0,
'DateTime' => 0,
},
);
$build->create_build_script;

6
config.example.json Normal file
View File

@ -0,0 +1,6 @@
{
"fullname": "Pepito el de los Palotes",
"dni": "12345678A",
"address": "Calle Salvador Allende S/N",
"postal_code": "13243"
}

16
example_bill.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
(
echo '8'
echo 'y'
echo '2'
echo 'Example concept'
echo '1'
echo '20.03'
echo 'y'
echo 'Example concept 2'
echo '2'
echo '20.03'
echo 'n'
) | perl -Ilib scripts/facturer.pl

View File

@ -0,0 +1,45 @@
package Owlcode::Tech::Facturer;
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use Moo;
use Path::Tiny;
use Mojo::JSON qw(decode_json encode_json);
has config => ( is => 'lazy' );
my $ROOT_PATH = path(__FILE__)->parent->parent->parent->parent->realpath;
sub fullname ($self) {
return $self->config->{fullname};
}
sub dni ($self) {
return $self->config->{dni};
}
sub address ($self) {
return $self->config->{address};
}
sub postal_code ($self) {
return $self->config->{postal_code};
}
sub _build_config {
my $config_path = $ROOT_PATH->child('config.json');
return decode_json( $config_path->slurp_utf8 );
}
sub run ($self) {
require Owlcode::Tech::Facturer::MainCommand;
my $main_command = Owlcode::Tech::Facturer::MainCommand->new;
$main_command->run;
}
1;

View File

@ -0,0 +1,352 @@
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';
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 $client_dni;
my $client_name;
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;
}
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 );
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,
number => $number,
date => $self->print_date($date),
kind_of_bill => $kind_of_bill,
SIMPLIFICADA => $SIMPLIFICADA,
COMPLETA => $COMPLETA,
concepts => $concepts,
final_data => $final_data,
}
)
);
}
sub template_bill ($self) {
my $text = <<'EOF';
<html>
<head>
</head>
<body>
<h1>Factura número <%= sprintf '%06d', $number %></h1>
<h2>Datos emisor.</h2>
<p>Emitida por <%= $self_name %> con DNI/CIF <%= $self_dni %> en fecha <%= $date %>.</p>
% if ($kind_of_bill eq $COMPLETA) {
<h2>Datos receptor.</h2>
<p>Destinatario <%= $client_name %> con DNI/CIF <%= $client_dni %>.</p>
% }
<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>
</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_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;

11
scripts/facturer.pl Normal file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env perl
use v5.36.0;
use strict;
use warnings;
use feature 'signatures';
use Owlcode::Tech::Facturer;
Owlcode::Tech::Facturer->new->run;