Skip to content

Instantly share code, notes, and snippets.

@juvuorin
Created April 28, 2021 17:15
Show Gist options
  • Save juvuorin/161411f41cba4842bbad017e91152eab to your computer and use it in GitHub Desktop.
Save juvuorin/161411f41cba4842bbad017e91152eab to your computer and use it in GitHub Desktop.
Koiramainen ohjelmointikisa 2021
/**
* Koiramainen Kirjanpito
*
* Tervetuloa Koiramaisen Kirjanpidon ohjeisiin. Alla olevassa koodissa määritellään kirjanpidon tilit.
* Automaatio on yksinkertainen. Lasketaan alv:t erikseen, samoin kaikki verottomat koiramaisuudet.
*
* Luo samaan hakemistoon tiedosto nimeltä 'input.txt' ja aseta sen sisälle kirjanpito aineisto
* seuraavassa muodossa:
*
* <Otsikko> <Otsikko>
* <Tilinumeto> <Rahamäärä>
*
* Esimerkiksi näin:
* Tilinumero Rahamäärä
* 4008 50.7
* 3010 400.6
* 6000 56
*
* Ohjelman ajamiseen tarvitset NodeJS ympäristön (vaikka versio 14.15.1 käy hyvin).
*
* Aja ohjelma komentokehotteessa käskyllä 'node index.js'.
*
* Ohjelma luo 'input.txt' tiedostossa olevasta kirjanpitomateriaalista 'output.txt' nimisen
* tiedoston, joka sisältää käsitellyn materiaalin valmiina kirjanpitoon vietäväksi.
*
* Voit testata ohjelman toimivuutta mukana tulevalla input.test.txt tiedostolla. Tämä tapahtuu
* käskyllä 'node index.js --test'. Käsky luo 'output.test.txt' tiedoston, jossa näet mallin
* millainen lopputulos tuotetusta materiaalista voisi tulla.
*
* Toivottavasti viihdyt ohjelman seurassa! :)
*/
// Tarvitsemme yhden extra-tiedoston, jolla voimme lukea luomiasi tiedostoja
const fs = require('fs');
/**
* Tilikirja
* Määrittele tähän kaikki tilit joita tarvitset ja määritä tilille tyyppi.
* Tyyppi voi olla 'noTax' tai 'tax'
* 'noTax' on tapahtumia, joista ei käsitellä veroja pois
* 'tax' on tapahtumia, joista käsitellään verot erikseen, tilin summa on veroton arvo
*
* On myös olemassa automaatisoituja tyyppejä:
* 'alv' tiliin lasketaan kaikki verot yhten
* 'beforeTax' tiliin lasketaan kaikki verotettavat tulot yhteen
* 'total' tiliin lasketaan kaikki summat yhteen
*/
const accounts = [
{ number: 4008, type: "noTax" },
{ number: 3010, type: "tax" },
{ number: 1700, type: "beforeTax" },
{ number: 2700, type: "alv", },
{ number: 6000, type: "noTax", },
{ number: 9000, type: "total", },
];
// Määritellään mitä automaatioita halutaan milloinkin tapahtuvan.
const relations = [
{ name: "noTax", relation: ["total", "noTax"] },
{ name: "tax", relation: ["beforeTax", "alv", "total"]},
];
// Asetuksia, esimerkiksi joku alv-niminen verottaja ja tiedoston nimi
const settings = {
inputFile: process.argv.slice(2)[0] === '--test' ? "input.test.txt" : "input.txt",
outputFile: process.argv.slice(2)[0] === '--test' ? "output.test.txt" : "output.txt",
alv: 24
}
const methodSelector = (name) => accounts.filter(acc => acc.type === name);
// Tämä funktio kattaa kaiken automaatioon liittyvän toiminnallisuuden
const accountFunctions = {
// Lisää summa verotettavien kokonaissaldoa laskeville tileille
beforeTax: (journal, data) => methodSelector("beforeTax").forEach(v => journal[v.number] = (journal[v.number] + data.sum).toFixed(2) * 1),
// Lisää summa kokonaissaldoa laskeville tileille
total: (journal, data) => methodSelector("total").forEach(v => journal[v.number] = (journal[v.number] + data.sum).toFixed(2) * 1),
// Lasketaan alvin osuus ja lisätään se alv tiliin. Lisäksi alkuperäisestä tilistä vähennetään vero.
alv: (journal, data) => {
methodSelector("alv").forEach(v => journal[v.number] = (journal[v.number] + (settings["alv"] * data.sum / (100 + settings["alv"])).toFixed(2) * 1).toFixed(2) * 1);
journal[data.account] += (100 * data.sum / (100 + settings["alv"])).toFixed(2) * 1;
},
// Veroja ei tästä ryhmästä tarvitse laskea, joten pelkkä summa lisätään tiliin
noTax: (journal, data) => journal[data.account] = journal[data.account] ? journal[data.account] += data.sum : data.sum
}
// Tämä funktio yhdistää annetut tiedot luomaasi tilikirjaan.
const journalFuntion = (journal) => {
accounts.map(acc => journal[acc.number] = 0);
return (data) => {
data.map(d => accounts
.filter(acc => acc.number === d.account)
.map(a => relations
.filter(relation => relation.name === a.type)
.map(rel => rel.relation
.map(r => accountFunctions[r](journal, d)))))
return journal;
}
}
// Nyt on aika lukea luomasi input tiedosto. Onhan kaikki tiedot oikein?
fs.readFile(`./${settings.inputFile}`, 'utf8', ((err, data) => {
if (err) {
console.log(`Tarkista, että hakemistossa on ${settings.inputFile} niminen tiedosto ja että se on oikeassa muodossa.`);
return
}
// Tässä kohtaa tiedostolle tehdään kaikenlaisia taikoja, jotta antamasi arvot saadaan käyttöön
const accountData = data.split("\n")
.map(r => r.split(" ")
.filter(row => row !== "")
.map(n => parseInt(n)))
.filter((_, i) => i !== 0)
.map(dat => ({ account: dat[0], sum: dat[1] }));
// Nyt antamasi tieto on valmis parsittavaksi
const dataSet = journalFuntion({})(accountData);
// Aika luoda uusi tiedosto annetuista tiedoista
let content = "Tilinumero Rahamäärä\n";
Object.keys(dataSet).forEach(key => {
content += `${key} ${dataSet[key]}\n`
});
// Ja sitten kirjoitetaan tiedot lasketut kirjanpitotiedot uuteen tiedostoon...ja vola!
fs.writeFile(`./${settings.outputFile}`, content, err => {
if (err) console.log(err);
});
}));
@juvuorin
Copy link
Author

Vastauksen tekijä kirjoittaa suvereenisti JavaScriptiä. Koodi on kauttaaltaan luettavaa ja hyvin kommentoitua. Merkkijonojen muodostamiseksi käytetään fiksusti merkkijonointerpolointia ($). Relations lista määrittelee näppärästi automaatiotoiminnot. Tekijä on myös ajatellut testausta ja ylläpitoa; tästä kielivät rivit 64-68, joissa otetaan huomioon komentoriviltä käynnistysvaiheessa luettu testilippu --test. Nuolifunktioiden käyttö on sujuvaa ja tekijä hallitsee hyvin myös JS-oliomallin - tästä kertoo olion kenttien iterointia rivillä 124.

Funktiot, kuten alv jne. on hyvin nimetty ja ne dokumentoituvat helposti tulkittaviksi. Tekijä pyöristää summat fiksusti kahden numeron tarkkuuteen (toFixed(2)), kuten asiaan kuuluu.

AccountFunctions on niputettu näppärästi nimettyyn JS-objektiin, joten ne ovat siististi omassa "nimiavaruudessaan". Riveillä 112-117 luetaan data näpsäkästi tiedostosta ja luodaan lopulta "tuple"-tyyppinen lista objekteja, joissa on tilin numero ja saldo - näppärää.

Const ja let -määreitä käytetään kauttaaltaan oikein, lisäksi ternary-operaattoria käytetään niin ikään fiksusti kautta linjan.

Erittäin siisti JS-ratkaisu, missä käytetään "jäsää" hyvän jäsämäisesti, luokkia vältellen ja hyvin dokumentoituvasti toimintoja nimettyihin olioihin ryhmitellen.

Hauska ratkaisu kaiken kaikkiaan!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment