The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#! perl --			-*- coding: utf-8 -*-

use utf8;

# MiniAdm.pm -- 
# Author          : Johan Vromans
# Created On      : Sun Oct  4 15:11:05 2009
# Last Modified By: Johan Vromans
# Last Modified On: Fri May  6 13:44:19 2011
# Update Count    : 111
# Status          : Unknown, Use with caution!

package main;

use strict;
use warnings;
use Encode;

our$cfg;

package EB::Tools::MiniAdm;

#use EB::Config;
use EB;

sub donotclobber {
    my ( $self, $opts ) = @_;

    my @files = qw( schema.dat opening.eb mutaties.eb relaties.eb );
    push( @files, $cfg->std_config );
    my $tally = 0;
    foreach ( @files ) {
	$tally++ if -f $_;
    }

    if ( $tally == @files ) {
	warn("?"._T("GESTOPT: Er is al een administratie aangemaakt")."\n");
	return;
    }
    if ( $tally ) {
	warn("?"._T("GESTOPT: Er is al een administratie gedeeltelijk aangemaakt")."\n");
	return;
    }
    return 1;
}

sub build {
    my ( $self, $opts ) = @_;

    return unless $self->donotclobber;
    return unless $self->sanitize($opts);

    # Generate.
    $self->generate_config($opts);
    $self->generate_schema($opts);
    $self->generate_relaties($opts);
    $self->generate_opening($opts);
    $self->generate_mutaties($opts);

    1;
}

sub sanitize {
    my ( $self, $opts ) = @_;

    $opts->{adm_naam}         ||= _T("Demo administratie");
    $opts->{adm_btwperiode}   ||= "jaar" if $opts->{has_btw};
    $opts->{adm_begindatum}   ||= 1900 + (localtime(time))[5];
    $opts->{adm_boekjaarcode} ||= 1900 + (localtime(time))[5];

    for ( qw(naam boekjaarcode) ) {
	$opts->{ "adm_$_" } =~ s/"/_/g;
    }

    $opts->{db_naam}          ||= "demoadm";
    $opts->{db_driver}        ||= "sqlite";

    1;
}

use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
use Encode;

sub generate_file {
    my ( $self, $file, $type, $opts, $writer ) = @_;

    if ( ! $opts->{_zip} && $opts->{template} ) {
	$opts->{_zip} = Archive::Zip->new();
	die( "?".__x("Probleem met het benaderen van {file}: {err}",
		     file => $opts->{template}, err => "$!")."\n" )
	  unless $opts->{_zip}->read( $opts->{template} ) == AZ_OK;
    }

    my $m;
    if ( $opts->{_zip} ) {
	$m = $opts->{_zip}->memberNamed($file);
    }

    my $fd;
    if ( $opts->{_zip} && $m ) {
	my $data = $opts->{_zip}->contents($m);
	die( "?".__x("Probleem met het aanmaken van {file}: Zip error",
		     file => $file)."\n" ) unless $data;

	#### TODO: Make more generic.
	if ( $file eq "opening.eb" ) {
	    for ( $data ) {
		s/^(\s*adm_naam\s+).*$        /$1"$opts->{adm_naam}"        /mgx;
		s/^(\s*adm_btwperiode\s+).*$  /$1"$opts->{adm_btwperiode}"  /mgx;
		s/^(\s*adm_begindatum\s+).*$  /$1"$opts->{adm_begindatum}"  /mgx;
		s/^(\s*adm_boekjaarcode\s+).*$/$1"$opts->{adm_boekjaarcode}"/mgx;
	    }
	}

	$data =~ s/\r//g;
	$data = decode_utf8($data);
	$data = [ split(/\n/, $data) ];
	$writer = sub { print { $fd } $_, "\n" foreach @$data };
	$type = undef;
    }

    open( $fd, '>:encoding(utf-8)', $file )
      or die( "?".__x("Probleem met het aanmaken van {file}: {err}",
		      file => $file, err => "$!")."\n" );
    if ( $type ) {
	print { $fd } ("# EekBoek $type\n",
		       "# Content-Type: text/plain; charset = UTF-8\n\n");
    }

    if ( $writer ) {
	$writer->( $self, $fd );
    }

    close( $fd )
      or die( "?".__x("Probleem met het afsluiten van {file}: {err}",
		      file => $file, err => "$!")."\n" );
}

sub generate_config {
    my ( $self, $opts ) = @_;

    return if exists $opts->{create_config} && !$opts->{create_config};

    my $fmt = "%-10.10s = %s\n";

    $self->generate_file
      ( $cfg->std_config, undef, $opts,
	sub {
	    my ( $self, $fd ) = @_;
	    if ( $opts->{lang} ) {
		print { $fd } ("[locale]\n");
		printf { $fd } ( $fmt, "lang", $opts->{lang} );
		print { $fd } ("\n");
	    }
	    print { $fd } ("[database]\n");
	    printf { $fd } ( $fmt, "name", $opts->{db_naam} );
	    foreach ( qw( driver host port user password path ) ) {
		next unless defined $opts->{"db_$_"};
		printf { $fd } ( $fmt, $_, $opts->{"db_$_"} )
	    }
	  }
      );
}

sub generate_schema {
    my ( $self, $opts ) = @_;

    return if exists $opts->{create_schema} && !$opts->{create_schema};

    # has_btw
    # has_crediteuren
    # has_crediteuren
    # has_kas
    # has_bank

    $self->generate_file
      ( "schema.dat", _T("Rekeningschema"), $opts,
	sub {
	    my ( $self, $fd ) = @_;
	    print { $fd } ( <<'EOD' );
# Dit bestand definiëert alle vaste gegevens van een administratie of
# groep administraties: het rekeningschema (balansrekeningen en
# resultaatrekeningen), de dagboeken en de BTW tarieven.
#
# Algemene syntaxregels:
#
# * Lege regels en regels die beginnen met een hekje # worden niet
#   geïnterpreteerd.
# * Een niet-ingesprongen tekst introduceert een nieuw onderdeel.
# * Alle ingesprongen regels zijn gegevens voor dat onderdeel.

# REKENINGSCHEMA
#
# Het rekeningschema is hiërarchisch opgezet volgende de beproefde
# methode Bakker. De hoofdverdichtingen lopen van 1 t/m 9, de
# verdichtingen t/m 99. De grootboekrekeningen zijn verdeeld in
# balansrekeningen en resultaatrekeningen.
#
# De omschrijving van de grootboekrekeningen wordt voorafgegaan door
# een vlaggetje, een letter die resp. Debet/Credit (voor
# balansrekeningen) en Kosten/Omzet/Neutraal (voor resultaatrekeningen)
# aangeeft. De omschrijving wordt indien nodig gevolgd door extra
EOD

	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<'EOD' );
# informatie. Voor grootboekrekeningen kan op deze wijze de BTW
# tariefstelling worden aangegeven die op deze rekening van toepassing
# is:
#
#   :btw=nul
#   :btw=hoog
#   :btw=laag
#   :btw=privé
#   :btw=anders
EOD
	    }
	    else {
		print { $fd } ( <<'EOD' );
# informatie.
EOD
	    }
	    print { $fd } ( <<'EOD' );
#
# Ook is het mogelijk aan te geven dat een rekening een koppeling
# (speciale betekenis) heeft met :koppeling=xxx. De volgende koppelingen
# zijn mogelijk:
#
EOD
	    if ( $opts->{has_crediteuren} ) {
		print { $fd } ( <<'EOD' );
#   crd		de standaard tegenrekening (Crediteuren) voor inkoopboekingen
EOD
	    }
	    if ( $opts->{has_debiteuren} ) {
		print { $fd } ( <<'EOD' );
#   deb		de standaard tegenrekening (Debiteuren) voor verkoopboekingen
EOD
	    }
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<'EOD' );
#   btw_ih	de rekening voor BTW boekingen voor inkopen, hoog tarief
#   btw_il	idem, laag tarief
#   btw_vh	idem, verkopen, hoog tarief
#   btw_vl	idem, laag tarief
#   btw_ph	idem, privé, hoog tarief
#   btw_pl	idem, laag tarief
#   btw_ah	idem, anders, hoog tarief
#   btw_al	idem, laag tarief
#   btw_ok	rekening voor de betaalde BTW
EOD
	    }
	    print { $fd } ( <<'EOD' );
#   winst	rekening waarop de winst wordt geboekt
#
# De koppeling winst is verplicht en moet altijd in een administratie
# voorkomen in verband met de jaarafsluiting.
EOD
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<'EOD' );
# De koppelingen voor BTW moeten worden opgegeven indien BTW
# van toepassing is op de administratie.
EOD
	    }
	    print { $fd } ( <<'EOD' );
# De koppelingen voor Crediteuren en Debiteuren moeten worden
# opgegeven indien er inkoop dan wel verkoopdagboeken zijn die gebruik
# maken van de standaardwaarden (dus zelf geen tegenrekening hebben
# opgegeven).

# Normaal lopen hoofdverdichtingen van 1 t/m 9, en verdichtingen
# van 10 t/m 99. Indien daarvan wordt afgeweken kan dit worden opgegeven
# met de opdracht "Verdichting". De twee getallen geven het hoogste
# nummer voor hoofdverdichtingen resp. verdichtingen.

Verdichting 9 99

# De nummers van de grootboekrekeningen worden geacht groter te zijn
# dan de maximale verdichting. Daarvan kan worden afgeweken door
# middels voorloopnullen de _lengte_ van het nummer groter te maken
# dan de lengte van de maximale verdichting. Als bijvoorbeeld 99 de
# maximale verdichting is, dan geeft 001 een grootboekrekening met
# nummer 1 aan.

Balansrekeningen

  1  Vaste Activa
     11  Materiële vaste activa

  2  Vlottende activa
     21  Handelsvoorraden
     22  Vorderingen
EOD
	    if ( $opts->{has_debiteuren} ) {
		print { $fd } ( <<'EOD' );
         2200  D   Debiteuren                                 :koppeling=deb
EOD
	    }
	    print { $fd } ( <<'EOD' );
     23  Liquide middelen
EOD
	    if ( $opts->{has_kas} ) {
		print { $fd } ( <<"EOD" );
         2300  D   Kas
EOD
	    }
	    if ( $opts->{has_bank} ) {
		print { $fd } ( <<"EOD" );
         2320  D   Bank
EOD
	    }
	print { $fd } ( <<"EOD" );
         2390  D   Kruisposten

  3  Eigen vermogen
     31  Kapitaal
         3100  C   Kapitaal de heer/mevrouw                   :koppeling=winst
         3110  C   Privé stortingen
         3120  D   Privé opnamen

  4  Vreemd vermogen
     41  Leveranciers kredieten
EOD
	    if ( $opts->{has_crediteuren} ) {
		print { $fd } ( <<'EOD' );
         4100  C   Crediteuren                                :koppeling=crd
EOD
	    }
	    print { $fd } ( <<'EOD' );
     42  Belastingen & soc. lasten
EOD
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<"EOD" );
         4200  C   BTW Verkoop Hoog                           :koppeling=btw_vh
         4210  C   BTW Verkoop Laag                           :koppeling=btw_vl
         4212  C   BTW Verkoop Privé                          :koppeling=btw_vp
         4214  C   BTW Verkoop Anders                         :koppeling=btw_va
         4220  D   BTW Inkoop Hoog                            :koppeling=btw_ih
         4230  D   BTW Inkoop Laag                            :koppeling=btw_il
         4232  D   BTW Inkoop Privé                           :koppeling=btw_ip
         4234  D   BTW Inkoop Anders                          :koppeling=btw_ia
         4290  C   Omzetbelasting betaald                     :koppeling=btw_ok
EOD
	    }

	    my $btw_hoog = "";
	    my $btw_laag = "";
	    if ( $opts->{has_btw} ) {
		$btw_hoog = ":btw=hoog";
		$btw_laag = ":btw=laag";
	    }
	    print { $fd } ( <<"EOD" );

Resultaatrekeningen

  6  Kosten
     61  Verkoopkosten
     62  Huisvestingskosten
     63  Bedrijfsvoering
     67  Contributies & abonnementen
     69  Algemene kosten
EOD
	    if ( $opts->{has_bank} ) {
		print { $fd } ( <<"EOD" );
         6980  K   Bankkosten
EOD
	    }
	    if ( $opts->{has_kas} ) {
		print { $fd } ( <<"EOD" );
         6981  K   Kasverschillen
EOD
	    }
	    print { $fd } ( <<"EOD" );

  8  Bedrijfsopbrengsten
     89	 Omzet Diversen
EOD
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<'EOD' );
         8900  O   Omzet diversen BTW hoog                    :btw=hoog
         8910  O   Omzet diversen BTW laag                    :btw=laag
         8920  O   Omzet diversen BTW vrij
EOD
	    }
	    print { $fd } ( <<"EOD" );

  9  Financiële baten & lasten
     91  Rente baten
EOD
	    if ( $opts->{has_bank} ) {
		print { $fd } ( <<"EOD" );
         9120  O   Rente bate Bank
EOD
	    }
	    print { $fd } ( <<"EOD" );
     92  Rente- en overige financiële lasten
EOD
	    if ( $opts->{has_bank} ) {
		print { $fd } ( <<"EOD" );
         9220  K   Rente last Bank
EOD
	    }
	    print { $fd } ( <<"EOD" );
     93  Overige baten
EOD
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<"EOD" );
         9390  O   Kleine ondernemersregeling
EOD
	    }
	    print { $fd } ( <<"EOD" );

# DAGBOEKEN
#
# EekBoek ondersteunt vijf soorten dagboeken: Kas, Bank, Inkoop,
# Verkoop en Memoriaal. Er kunnen een in principe onbeperkt aantal
# dagboeken worden aangemaakt.
# In de eerste kolom wordt de korte naam (code) voor het dagboek
# opgegeven. Verder moet voor elk dagboek worden opgegeven van welk
# type het is. Voor dagboeken van het type Kas en Bank moet een
# tegenrekening worden opgegeven, voor de overige dagboeken mag een
# tegenrekening worden opgegeven.
# De optie :dc kan worden gebruikt om aan te geven dat het journaal
# voor dit dagboek de boekstuktotalen in gescheiden debet en credit
# moet tonen.

Dagboeken

EOD
	    if ( $opts->{has_crediteuren} ) {
		print { $fd } ( <<"EOD" );
  I     Inkoop                :type=inkoop
EOD
	    }
	    if ( $opts->{has_debiteuren} ) {
		print { $fd } ( <<"EOD" );
  V     Verkoop               :type=verkoop
EOD
	    }
	    if ( $opts->{has_kas} ) {
		print { $fd } ( <<"EOD" );
  K     Kas                   :type=kas        :rekening=2300
EOD
	    }
	    if ( $opts->{has_bank} ) {
		print { $fd } ( <<"EOD" );
  B     Bank                  :type=bank       :rekening=2320
EOD
	    }
	    print { $fd } ( <<"EOD" );
  M     Memoriaal             :type=memoriaal
EOD
	    if ( $opts->{has_btw} ) {
		print { $fd } ( <<"EOD" );

# BTW TARIEVEN
#
# Er zijn vijf tariefgroepen: "hoog", "laag", "nul", "privé" en
# "anders". De tariefgroep bepaalt het rekeningnummer waarop de
# betreffende boeking plaatsvindt.
# Binnen elke tariefgroep zijn meerdere tarieven mogelijk, hoewel dit
# in de praktijk niet snel zal voorkomen.
# In de eerste kolom wordt de (numerieke) code voor dit tarief
# opgegeven. Deze kan o.m. worden gebruikt om expliciet een BTW tarief
# op te geven bij het boeken. Voor elk gebruikt tarief (behalve die
# van groep "nul") moet het percentage worden opgegeven. Met de
# aanduiding :exclusief kan worden opgegeven dat boekingen op
# rekeningen met deze tariefgroep standaard het bedrag exclusief BTW
# aangeven.
#
# BELANGRIJK: Mutaties die middels de command line shell of de API
# worden uitgevoerd maken gebruik van het geassocieerde BTW tarief van
# de grootboekrekeningen. Wijzigingen hierin kunnen dus consequenties
# hebben voor de reeds in scripts vastgelegde boekingen.

BTW Tarieven

   0  BTW 0%                 :tariefgroep=nul
   1  BTW 19% incl.          :tariefgroep=hoog :perc=19,00
   2  BTW 19% excl.          :tariefgroep=hoog :perc=19,00 :exclusief
   3  BTW 6,0% incl.         :tariefgroep=laag :perc=6,00
   4  BTW 6,0% excl.         :tariefgroep=laag :perc=6,00 :exclusief
   5  BTW Privé 12% incl.    :tariefgroep=privé :perc=12,00
   6  BTW Privé 12% ex.	     :tariefgroep=privé :perc=12,00 :exclusief
EOD
	    }
	    print { $fd } ( <<"EOD" );

# Einde EekBoek schema
EOD
	} );
}

sub generate_relaties {
    my ( $self, $opts ) = @_;

    return if exists $opts->{create_relaties} && !$opts->{create_relaties};

    $self->generate_file( "relaties.eb", _T("Relaties"), $opts );
}

sub generate_opening {
    my ( $self, $opts ) = @_; 

    return if exists $opts->{create_opening} && !$opts->{create_opening};

    $self->generate_file
      ( "opening.eb", _T("Opening"), $opts,
	sub {
	    my ( $self, $fd ) = @_;
	    print { $fd }
	      ( "adm_naam \"", $opts->{adm_naam}, "\"\n" );
	    print { $fd }
	      ( "adm_btwperiode ", $opts->{adm_btwperiode}, "\n" )
		if $opts->{has_btw};
	    print { $fd }
	      ( "adm_begindatum \"", $opts->{adm_begindatum}, "\"\n" );
	    print { $fd }
	      ( "adm_boekjaarcode \"", $opts->{adm_boekjaarcode}, "\"\n" );
	    print { $fd }
	      ( "adm_open\n");
	  }
      );
}

sub generate_mutaties {
    my ( $self, $opts ) = @_;

    return if exists $opts->{create_mutaties} && !$opts->{create_mutaties};

    $self->generate_file( "mutaties.eb", _T("Mutaties"), $opts );
}

1;