Diferència entre revisions de la pàgina «Aplicació de facturació trimestral»

De wikijoan
Salta a la navegació Salta a la cerca
 
(Hi ha una revisió intermèdia del mateix usuari que no es mostren)
Línia 3: Línia 3:
 
Tenim una empresa amb 4 delegacions, i volem portar el control de la facturació trimestral de cadascuna de les delegacions.
 
Tenim una empresa amb 4 delegacions, i volem portar el control de la facturació trimestral de cadascuna de les delegacions.
  
Fer una petita aplicació web amb les següents especificacions. La interfície web consta d'una taula a l'esquerra, i un petit formulari a la dreta. La taula consta de tres camps i 4 files amb les següents dades:
+
Fer una petita aplicació web amb les següents especificacions. La interfície web consta d'una taula a dalt, i un petit formulari a baix, tal com es veu a la imatge. La taula consta de tres camps i 4 files amb les següents dades:
  
 
*capçalera: sucursal, nom, telefon, mail
 
*capçalera: sucursal, nom, telefon, mail
Línia 17: Línia 17:
 
*'''Sucursal. Camp de text que no es pot escriure (atribut ''readonly'').
 
*'''Sucursal. Camp de text que no es pot escriure (atribut ''readonly'').
 
*'''Nom. Camp de text que no es pot escriure (atribut ''readonly'').
 
*'''Nom. Camp de text que no es pot escriure (atribut ''readonly'').
*'''Any'''. Llista desplegable amb els següents valors: 2015, 2016, 2017, 2018, 2019.
+
*'''Any'''. Llista desplegable amb els quatre últims anys: 2021, 2020, 2019, 2018.
 
*'''Trimestre'''. Llista desplegable amb els següents valors: 1T, 2T, 3T, 4T (que es correspon amb els 4 trimestres).
 
*'''Trimestre'''. Llista desplegable amb els següents valors: 1T, 2T, 3T, 4T (que es correspon amb els 4 trimestres).
 
*'''Facturació'''. Caixa de text. Regla de validació: número que admet dues posicions decimals.
 
*'''Facturació'''. Caixa de text. Regla de validació: número que admet dues posicions decimals.
  
Es tracta d'omplir semi-automàticament els camps del formulari i enviar les dades del formulari a una taula de MySQL. Quan cliquem sobre la sucursal en la taula se'ns omplirà automàticament els camps ''sucursal'' i ''nom'' del formulari. Aleshores seleccionarem un any i trimestre, posarem un valor decimal de facturació, i només quan totes les dades estiguin plenes (regla de validació global per a tot el formulari), enviarem les dades per ser inserides en la taula '''jbalmes.facturacio''' ''(sucursal, nom, year, trimestre, euros)''.
+
Es tracta d'omplir semi-automàticament els camps del formulari clicant el link que hi ha a la taula. Quan cliquem sobre la sucursal en la taula se'ns omplirà automàticament els camps ''sucursal'' i ''nom'' del formulari. Aleshores seleccionarem un any i trimestre, posarem un valor decimal de facturació, i només quan totes les dades estiguin plenes (regla de validació global per a tot el formulari), enviaríem les dades al servidor.
  
*l'aplicatiu ha de tenir
+
Es fa una demostració a classe d'una possible solució.
A continuació se't mostra una estructura possible de l'aplicatiu web, sense cap mena de full d'estils.
+
=Tasques=
<pre>
+
La teva solució ha d'incloure entre d'altres les següents millores:
CREATE TABLE facturacio(
+
*fitxer d'estils CSS extern. Treballar la capa de disseny i maquetació. Concretament, la gestió de la validació dels camps del formulari.
sucursal varchar(30) not null,
 
nom varchar(30) not null,
 
year smallint not null,
 
trimestre varchar(2) not null,
 
euros decimal(10,2) not null
 
);
 
 
 
insert into facturacio values ('Barcelona', 'Arnau Guinovart', 2015, '1T', 1854.34);
 
</pre>
 
 
 
=Solució parcial=
 
'''NOTA''': El professor et presenta una possible solució. Ara bé, la teva solució ha d'incloure entre d'altres les següents millores:
 
*fitxer d'estils CSS extern.
 
 
*fitxer de funcions javascript extern.
 
*fitxer de funcions javascript extern.
*els events javascript que implementis els has de definir en un fitxer .js extern amb el mètode ''addEventListener''.
+
*els events amb''addEventListener''.
 +
*Crear les opcions dels anys i les opcions dels trimestres programàticament (''createElement'').
 +
*Posar automàticament l'any actual (segons la data del sistema), i els 3 anys anteriors, de forma programàtica.
 +
*Posar automàticament el trimestre anterior (segons la data del sistema). És a dir, si estem a desembre de 2021 (estem en el 4T), hem de posar l'any 2021 i el 3T (trimestre anterior). Si fóssim el gener de 2022 posaríem 2021 i 4T.
  
script '''facturacio.php''':
+
==Opcional==
 +
Enviar les dades de facturació a la taula de la base de dades FACTURACIO, en un servidor web amb servidor mysql.
 +
==Solucion parcials==
 +
Per tal d'assignar de forma dinàmica els events a tots els links podem fer:
 
<pre>
 
<pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
...
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es">
+
var anchors = document.getElementsByTagName("a");
<head>
 
<meta charset="UTF-8" />
 
<title>Formulari de contactes (amb varis mails)</title>
 
  
<script>
+
for (var i=0; i<anchors.length; i++) { //recorrem tots els links
function escriure_formulari(obj) {
+
//posem els events associats als links
console.log(obj.text);
+
anchors[i].addEventListener("click", function () {escriure_formulari(this);});
var taula = document.getElementById("taula_sucursals");
 
var files = taula.rows;
 
for (i=1;i<files.length; i++) {
 
//alert(files[i].cells[0].innerHTML);
 
str = files[i].cells[0].innerHTML;
 
if (str.indexOf(obj.text) > 0) {
 
//document.frm_facturacio.sucursal.value = obj.text;
 
document.frm_facturacio.sucursal.value = obj.innerHTML;
 
document.frm_facturacio.nom.value = files[i].cells[1].innerHTML;
 
}
 
 
 
 
 
}
 
}
 
 
 
function validar_numero_2decimals(obj){
 
val = obj.value;
 
//alert(val);
 
 
 
var patro=/^\d+(\.\d{1,2})?$/;
 
if(!patro.test(val)){
 
obj.value = "";
 
return false;
 
}else{
 
return true;
 
}
 
}
 
 
 
function controlar_camps_formulari(){
 
forms = document.getElementsByTagName("form");
 
for (i=0;i<forms[0].elements.length-1;i++) {
 
//alert(forms[0].elements[i].value);
 
if (forms[0].elements[i].value=="") {
 
alert("camp buit: " + forms[0].elements[i].name);
 
return false;
 
}
 
}
 
 
 
 
 
}
 
 
 
</script>
 
</head>
 
<body>
 
<?php
 
 
 
if (isset($_POST['sucursal'])) {
 
$vsucursal=$_POST['sucursal'];
 
$vnom=$_POST['nom'];
 
$vyear=$_POST['year'];
 
$vtrimestre=$_POST['trimestre'];
 
$veuros=$_POST['euros'];
 
 
 
if ($vsucursal!="") {
 
include("open_db.php");
 
 
 
$sqlstring  = "INSERT INTO facturacio ";
 
$sqlstring .= "(sucursal, nom, year, trimestre, euros) values (";
 
$sqlstring .= "'" . $vsucursal . "','" . $vnom . "'," . $vyear . ",'" . $vtrimestre . "'," . $veuros . ")";
 
 
 
//echo $sqlstring."<br />";
 
//$result = mysqli_query($conn, $sqlstring);
 
 
 
    if (!($result = mysqli_query($conn, $sqlstring))) {
 
        echo "Error: ".mysqli_error($conn)."<br />";
 
echo "NO s'ha inserit el valor a la base de dades.<br />";
 
    } else {
 
 
 
echo "S'ha inserit el valor a la base de dades.<br />";
 
}
 
 
 
include("close_db.php");
 
}
 
}
 
?>
 
 
 
<h1>Facturació per delegacions</h1>
 
<table id="taula_sucursals" border="1">
 
<tr><td><b>Delegació</b></td><td><b>Nom</b></td><td><b>Telèfon</b></td><td><b>Mail</b></td></tr>
 
<tr><td><a href="#" onclick="escriure_formulari(this)">Barcelona</a></td><td>Arnau Guinovart</td><td>93.345.54.45</td><td>aguinovart@gmail.com</td></tr>
 
<tr><td><a href="#" onclick="escriure_formulari(this)">Tarragona</a></td><td>Maria Peris</td><td>977.34.34.22</td><td>mperis@gmail.com</td></tr>
 
<tr><td><a href="#" onclick="escriure_formulari(this)">Lleida</a></td><td>Josep Anglerill</td><td>973.33.44.55</td><td>janglerill@gmail.com</td></tr>
 
<tr><td><a href="#" onclick="escriure_formulari(this)">Girona</a></td><td>Montserrat Sirera</td><td>972.44.55.66</td><td>msirera@gmail.com</td></tr>
 
</table>
 
<br />
 
<form name="frm_facturacio" method="post" onsubmit="controlar_camps_formulari()" action="facturacio.php" >
 
<label>Sucursal: </label> <input type="text" name="sucursal" readonly />
 
<br />
 
 
 
<label>Nom: </label> <input type="text" name="nom" readonly />
 
<br />
 
 
 
<label>Any: </label>
 
<select name="year" >
 
<option value="2017">2017</option>
 
<option value="2018">2018</option>
 
<option value="2019">2019</option>
 
<option value="2020" selected="selected">2020</option>
 
</select>
 
<br />
 
 
 
<label>Trimestre: </label>
 
<select name="trimestre" >
 
<option value="1">1T</option>
 
<option value="2">2T</option>
 
<option value="3">3T</option>
 
<option value="4">4T</option>
 
</select>
 
<br />
 
 
 
<label>Facturació: </label> <input type="text"  name="euros" onchange="validar_numero_2decimals(this)" /> (format: punt decimal)
 
<br />
 
 
 
<input type="submit" value="Enviar" />
 
</form>
 
 
 
</body>
 
</html>
 
</pre>
 
script '''open.php''':
 
<pre>
 
<?php
 
$conn = mysqli_connect("localhost", "alumne", "keiL2lai", "jbalmes");
 
if (!$conn) {
 
    $log->error('Could not connect: ' . mysql_error());
 
    die('Could not connect: ' . mysql_error());
 
}
 
 
 
mysqli_set_charset($conn,'utf8');
 
?>
 
</pre>
 
script '''close.php''':
 
<pre>
 
<?php
 
mysqli_close($conn);
 
?>
 
</pre>
 
==Solució addEventListener==
 
<pre>
 
...
 
<script>
 
...
 
function carregar_events(){
 
var enllacos = document.links;
 
for (i=0;i<enllacos.length; i++) {
 
//alert(enllacos[i].href);
 
enllacos[i].addEventListener("click", function(){escriure_formulari(this)});
 
 
}
 
}
}
 
...
 
</script>
 
 
...
 
...
 
<body onload="carregar_events()">
 
<body onload="carregar_events()">
Línia 212: Línia 52:
 
<pre>
 
<pre>
 
...
 
...
<script>
 
...
 
function carregar_events(){
 
 
var taula = document.getElementById("taula_sucursals");
 
var taula = document.getElementById("taula_sucursals");
 
var files = taula.rows;
 
var files = taula.rows;
Línia 222: Línia 59:
 
}
 
}
 
...
 
...
</script>
 
...
 
<body onload="carregar_events()">
 
....
 
 
</pre>
 
</pre>
 
En aquest cas hem de pensar el nostre document com si fos XML (veure ''childNodes''). És la manera més general de recórrer l'estructura d'una document web.
 
En aquest cas hem de pensar el nostre document com si fos XML (veure ''childNodes''). És la manera més general de recórrer l'estructura d'una document web.
  
==Millores a implementar==
+
Per crear les opcions de la llista desplegable programàticament:
*Solució amb ''addEventListener''.
 
*Posar automàticament l'any actual (segons la data del sistema)
 
*Posar automàticament el trimestre actual (segons la data del sistema)
 
*Ficar el codi javascript en un fitxer extern
 
*Ficar un full d'estils extern, i treballar la capa de disseny i maquetació. Concretament, la gestió de la validació dels camps del formulari.
 
*Eliminar el botó Enviar. Després de posar el valor de la facturació, ja s'envien les dades del formulari al servidor.
 
*En el servidor, si es tornen a enviar les dades corresponents a un any i trimestre, fer un update en comptes d'un insert.
 
*petit script informe.php: per cada any, avariguar quina sucursal ha facturat més diners.
 
*etc...
 
==Solució 2==
 
En aquesta solució hem eliminat tota la part de PHP i ens centrem en la part de javascript (el front-end). Objectius:
 
*volem separar totalment la part de html i la parts de javascript
 
*volem que el codi funcioni per al 2020, 2021,..., 2050. Han d'aparèixer els últims 4 anys a partir de l'actual.
 
*Volem que l'any i el trimestre surtin bé (en el sentit de portar la comptabilitat d'una empresa). Si ara estem a gener/febrer de 2021, l'últim trimestre tancat és el 4rt trimestre de 2020: 4T 2020.
 
*Tots els events amb ''addEventListener''.
 
 
 
Us passo dos fitxers de solució:
 
 
 
script ''facturacio.html'':
 
 
<pre>
 
<pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
...
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es">
 
<head>
 
<meta charset="UTF-8" />
 
<title>Formulari de contactes (amb varis mails)</title>
 
 
 
</head>
 
 
 
<body>
 
 
 
<h1>Facturació per delegacions</h1>
 
<table id="taula_sucursals" border="1">
 
<tr><td><b>Delegació</b></td><td><b>Nom</b></td><td><b>Telèfon</b></td><td><b>Mail</b></td></tr>
 
<tr><td><a href="#">Barcelona</a></td><td>Arnau Guinovart</td><td>93.345.54.45</td><td>aguinovart@gmail.com</td></tr>
 
<tr><td><a href="#">Tarragona</a></td><td>Maria Peris</td><td>977.34.34.22</td><td>mperis@gmail.com</td></tr>
 
<tr><td><a href="#">Lleida</a></td><td>Josep Anglerill</td><td>973.33.44.55</td><td>janglerill@gmail.com</td></tr>
 
<tr><td><a href="#">Girona</a></td><td>Montserrat Sirera</td><td>972.44.55.66</td><td>msirera@gmail.com</td></tr>
 
</table>
 
<br />
 
<form name="frm_facturacio" method="post" action="facturacio.php" >
 
<label>Sucursal: </label> <input type="text" name="sucursal" readonly />
 
<br />
 
 
 
<label>Nom: </label> <input type="text" name="nom" readonly />
 
<br />
 
 
 
<label>Any: </label>
 
<select name="year" id="year" >
 
</select>
 
<br />
 
 
 
<label>Trimestre: </label>
 
<select name="trimestre" id="trimestre">
 
<option value="1">1T</option>
 
<option value="2">2T</option>
 
<option value="3">3T</option>
 
<option value="4">4T</option>
 
</select>
 
<br />
 
 
 
<label>Facturació: </label> <input type="text"  name="euros" /> (format: punt decimal)
 
<br />
 
 
 
<input type="submit" value="Enviar" />
 
</form>
 
 
 
</body>
 
</html>
 
 
 
<!-- important al final -->
 
<script src="script.js"></script>
 
</pre>
 
 
 
script ''script.js'':
 
<pre>
 
//s'executa quan s'ha carregat la pàgina
 
document.body.addEventListener("load", inici(), false);
 
 
 
function inici() {
 
document.forms[0].euros.addEventListener("change",
 
        function (event) {
 
            event.preventDefault();
 
            validar_numero_2decimals(this);
 
        },
 
        false);
 
 
 
document.forms[0].addEventListener("submit",
 
        function (event) {
 
            event.preventDefault();
 
            controlar_camps_formulari();
 
        },
 
        false);
 
 
 
var year = new Date().getFullYear()-1;
 
var month = new Date().getMonth();
 
//console.log(year);
 
var trimestre;
 
if (month>=0 && month<=2) {//hem de posar el trimestre anterior. Si ara estem al trimestre 1, hem de posar el 4
 
trimestre = 4;
 
} else if (month>=3 && month<=5) {
 
trimestre = 1;
 
} else if (month>=6 && month<=8) {
 
trimestre = 2;
 
} else {
 
trimestre = 3;
 
}
 
 
 
 
//creem les opcions de la select
 
//creem les opcions de la select
 
var sel_year = document.getElementById("year");
 
var sel_year = document.getElementById("year");
Línia 353: Línia 81:
 
    sel_year.appendChild(option);
 
    sel_year.appendChild(option);
 
}
 
}
 
+
...
//console.log(trimestre);
 
 
 
var sel_trimestre = document.getElementById("trimestre");
 
var opcions = sel_trimestre.childNodes;
 
//console.log(opcions)
 
for(var i=0; i<opcions.length; i++) {
 
    //console.log(opcions[i].nodeType);
 
    if (opcions[i].nodeType==1) {
 
    //console.log(opcions[i].value);
 
    if(opcions[i].value == trimestre) { //hem de posar selected
 
    opcions[i].selected = "selected"
 
    }
 
    }
 
}
 
 
 
//links
 
//https://stackoverflow.com/questions/6041593/adding-click-event-via-addeventlistener-to-confirm-navigation-from-a-hyperlink
 
var links = document.links;
 
var anchors = document.getElementsByTagName("a");
 
//console.log(anchors.length);
 
for (var i=0; i<anchors.length; i++) { //recorrem tots els links
 
//posem els events associats als links
 
anchors[i].addEventListener("click",
 
        function (event) {
 
            event.preventDefault();
 
            escriure_formulari(this);
 
        },
 
        false);
 
}
 
}
 
 
 
function escriure_formulari(obj) {
 
console.log(obj.text);
 
var taula = document.getElementById("taula_sucursals");
 
var files = taula.rows;
 
for (i=1;i<files.length; i++) {
 
//alert(files[i].cells[0].innerHTML);
 
str = files[i].cells[0].innerHTML;
 
if (str.indexOf(obj.text) > 0) {
 
//document.frm_facturacio.sucursal.value = obj.text;
 
document.frm_facturacio.sucursal.value = obj.innerHTML;
 
document.frm_facturacio.nom.value = files[i].cells[1].innerHTML;
 
}
 
 
 
}
 
}
 
 
 
function validar_numero_2decimals(obj){
 
val = obj.value;
 
//alert(val);
 
 
 
var patro=/^\d+(\.\d{1,2})?$/;
 
if(!patro.test(val)){
 
obj.value = "";
 
return false;
 
}else{
 
return true;
 
}
 
}
 
 
 
function controlar_camps_formulari(){
 
forms = document.getElementsByTagName("form");
 
for (i=0;i<forms[0].elements.length-1;i++) {
 
//alert(forms[0].elements[i].value);
 
if (forms[0].elements[i].value=="") {
 
alert("camp buit: " + forms[0].elements[i].name);
 
return false;
 
}
 
}
 
 
 
}
 
 
</pre>
 
</pre>
 
 
=Entrega=
 
=Entrega=
Els alumnes entregaran al Google Classroom tots els fitxers generats. S'empaquetaran tots aquests fitxers i es pujaran al Google Classroom dins del termini d'entrega de la pràctica.
+
Li ensenyaràs al professor la teva solució (nota de classe).
  
Recorda la '''normativa''' per entregar les pràctiques al Schoology: [[ASIX-M10-UF2#Normativa_d.27entrega_de_les_pr.C3.A0ctiques_al_Schoology]]
+
Entregaràs en el Classroom la teva solució, amb les captures de pantalla que siguin l'evidència de què has fet tu el codi, que has implementat les tasques que se't demanen, i que s'executa en la teva màquina.
  
{{Autor}}, gener 2021
+
{{Autor}}, desembre 2021
 
[[Categoria: Institut Jaume Balmes]]
 
[[Categoria: Institut Jaume Balmes]]
 
[[Categoria: Assignatura DAW-M06-WEC]]
 
[[Categoria: Assignatura DAW-M06-WEC]]

Revisió de 09:53, 21 des 2021

Enunciat del problema

Formulari aplicacio facturacio.jpg

Tenim una empresa amb 4 delegacions, i volem portar el control de la facturació trimestral de cadascuna de les delegacions.

Fer una petita aplicació web amb les següents especificacions. La interfície web consta d'una taula a dalt, i un petit formulari a baix, tal com es veu a la imatge. La taula consta de tres camps i 4 files amb les següents dades:

  • capçalera: sucursal, nom, telefon, mail
  • dades:
    • Barcelona;Arnau Guinovart;93.345.54.45;aguinovart@gmail.com
    • Tarragona;Maria Peris;977.34.34.22;mperis@gmail.com
    • Lleida;Josep Anglerill;973.33.44.55;janglerill@gmail.com
    • Girona;Montserrat Sirera;972.44.55.66;msirera@gmail.com

El camp sucursal és un link. Quan cliquem sobre la sucursal s'executarà la funció de Javascript: escriure_formulari().

El formulari consta dels següents camps:

  • Sucursal. Camp de text que no es pot escriure (atribut readonly).
  • Nom. Camp de text que no es pot escriure (atribut readonly).
  • Any. Llista desplegable amb els quatre últims anys: 2021, 2020, 2019, 2018.
  • Trimestre. Llista desplegable amb els següents valors: 1T, 2T, 3T, 4T (que es correspon amb els 4 trimestres).
  • Facturació. Caixa de text. Regla de validació: número que admet dues posicions decimals.

Es tracta d'omplir semi-automàticament els camps del formulari clicant el link que hi ha a la taula. Quan cliquem sobre la sucursal en la taula se'ns omplirà automàticament els camps sucursal i nom del formulari. Aleshores seleccionarem un any i trimestre, posarem un valor decimal de facturació, i només quan totes les dades estiguin plenes (regla de validació global per a tot el formulari), enviaríem les dades al servidor.

Es fa una demostració a classe d'una possible solució.

Tasques

La teva solució ha d'incloure entre d'altres les següents millores:

  • fitxer d'estils CSS extern. Treballar la capa de disseny i maquetació. Concretament, la gestió de la validació dels camps del formulari.
  • fitxer de funcions javascript extern.
  • els events ambaddEventListener.
  • Crear les opcions dels anys i les opcions dels trimestres programàticament (createElement).
  • Posar automàticament l'any actual (segons la data del sistema), i els 3 anys anteriors, de forma programàtica.
  • Posar automàticament el trimestre anterior (segons la data del sistema). És a dir, si estem a desembre de 2021 (estem en el 4T), hem de posar l'any 2021 i el 3T (trimestre anterior). Si fóssim el gener de 2022 posaríem 2021 i 4T.

Opcional

Enviar les dades de facturació a la taula de la base de dades FACTURACIO, en un servidor web amb servidor mysql.

Solucion parcials

Per tal d'assignar de forma dinàmica els events a tots els links podem fer:

...
	var anchors = document.getElementsByTagName("a");

	for (var i=0; i<anchors.length; i++) { //recorrem tots els links
		//posem els events associats als links
		anchors[i].addEventListener("click", function () {escriure_formulari(this);});
	}
...
<body onload="carregar_events()">
...

i també ho podem fer d'una manera més genèrica, cercant els enllaços que hi ha dins de les cel.les:

...
	var taula = document.getElementById("taula_sucursals");
	var files = taula.rows;
	for (i=1;i<files.length; i++) {
		files[i].children[0].children[0].addEventListener("click", function(){escriure_formulari(this)});
	}
}
...

En aquest cas hem de pensar el nostre document com si fos XML (veure childNodes). És la manera més general de recórrer l'estructura d'una document web.

Per crear les opcions de la llista desplegable programàticament:

...
	//creem les opcions de la select
	var sel_year = document.getElementById("year");

	//Create and append the options
	for (var i = 0; i < 4; i++) {
	    var option = document.createElement("option");
	    option.value = year-i;
	    option.text = year-i;
	    console.log(year-i)
	    if (trimestre==4 && i==1) {
		    option.selected = "selected"
		} else if (i==year) {
		    option.selected = "selected"
		}
	    sel_year.appendChild(option);
	}
...

Entrega

Li ensenyaràs al professor la teva solució (nota de classe).

Entregaràs en el Classroom la teva solució, amb les captures de pantalla que siguin l'evidència de què has fet tu el codi, que has implementat les tasques que se't demanen, i que s'executa en la teva màquina.


creat per Joan Quintana Compte, desembre 2021