#!/usr/bin/perl
# Internetový obchod s hrami / košík. Veškeré výpočty ceny včetně slev a poštovného se dějí tady.
# (c) 2007 Dan Zeman <zeman@ufal.mff.cuni.cz>
# Licence: GNU GPL

package kosik;
use utf8;
use Encode;
use pomoc;
use dzsql;



BEGIN
{
    # Globální konstanty
    $glob_mnozstevni_sleva_nad = 6000; # při nákupu nad tolik Kč je množstevní sleva
    $glob_mnozstevni_sleva     = 3; # procenta
    # Nový (19.4.2008) způsob výpočtu poštovného:
    # Počet řádků tabulky lze měnit. Měly by však být seřazené podle max ceny zboží.
    # Řádek říká, kolik stojí poštovné, když cena zasílaného zboží nepřekročí určitou hodnotu.
    @glob_postovne =
    ( # legenda: max cena zboží, poštovné dobírka, poštovné předplatba
      # je-li cena vyšší než poslední řádek tabulky, je poštovné zdarma
        [ 300,  89, 59],
        [ 500,  99, 69],
        [1000, 109, 79],
        [3000, 118, 89]
    );
    $glob_priplatek_zrychlene = 30; # doručení do týdne (jen u her, které jsou skladem)
    $glob_priplatek_expres = 50; # doručení do 2 pracovních dnů (jen u her, které jsou skladem)
}



#------------------------------------------------------------------------------
# Převede obsah košíku z řetězcového zápisu používaného v parametrech CGI
# (1x37a5x152) na pole hashů. Nic jiného nedělá, tedy se ani nesnaží dívat do
# databáze na názvy zboží. Díky tomu ale taky nepotřebuje odkaz na databázi ani
# ostatní parametry CGI.
#------------------------------------------------------------------------------
sub dekodovat
{
    my $kosik = shift; # řetězcový zápis obsahu košíku
    my @kosik = split(/a/, $kosik);
    # Posčítat počty jednotlivých druhů zboží.
    # (Jeden druh zboží se v zápisu mohl vyskytnout opakovaně.)
    my %kosik;
    foreach my $radek (@kosik)
    {
        my ($pocet, $vec) = split(/x/, $radek);
        $kosik{$vec} += $pocet;
    }
    # Nyní převést hash na pole, protože košík budeme chtít procházet, ne v něm vyhledávat.
    splice(@kosik);
    my @klice = sort {$a <=> $b} (keys(%kosik));
    foreach my $klic (@klice)
    {
        my %zaznam =
        (
            "kod" => $klic,
            "pocet" => $kosik{$klic}
        );
        push(@kosik, \%zaznam);
    }
    return \@kosik;
}



#------------------------------------------------------------------------------
# Převede obsah košíku z pole hashů na řetězcový zápis používaný v parametrech
# CGI (1x37a5x152).
#------------------------------------------------------------------------------
sub zakodovat
{
    my $kosik = shift; # odkaz na pole hashů
    my $zkosik = join("a", map {$_->{pocet}."x".$_->{kod}} grep {$_->{pocet}>0} @{$kosik});
    return $zkosik;
}



#------------------------------------------------------------------------------
# Zjistí v databázi názvy a ceny zboží a připíše je k záznamům o zboží
# v košíku. Potřebuje znát parametry CGI, aby věděla, jaké slevy se mají na
# zboží aplikovat. Vlastní košík však z parametrů CGI nečte, ten chce dostat už
# dekódovaný. Do zvláštního hashe pak zapíše další dopočítané vlastnosti
# objednávky (celkovou cenu, množstevní slevy apod.)
#------------------------------------------------------------------------------
sub dopocitat
{
    my $databaze = shift;
    my $kosik = shift; # odkaz na pole hashů (dekódovaný košík)
    ###!!! Pozor! 14.12.2007 jsem náhodou zjistil, že tato funkce se zřejmě často volá, když je košík prázdný!
    ###!!! Pokud nejde zabránit přímo těmto voláním, měl bych je alespoň detekovat tady a ihned se vrátit.
    ###!!! Teď zatěžuju server stovkami zbytečných dotazů do databáze (viz níže, kde filtr na zboží může být prázdný).
    my $objednavka = shift; # odkaz na cílový hash
    # Nahashovat zboží v košíku pro snadný přístup.
    my %kosik;
    foreach my $vec (@{$kosik})
    {
        $kosik{$vec->{kod}} = $vec;
    }
    # Byl zadán slevový kód? Pokud je platný, nastavit výši slevy.
    # Zatím byly vydány tyto slevové kódy (bez rozlišení malých a velkých písmen):
    # DES7121, ZAK7121
    # Oba platí do 1.12.2007 a způsobují slevu 5 %. Není možné je aplikovat na klubové, členské ani hermanské ceny.
    # Slevu také nelze použít na akční zboží (má zaškrtnuté políčko sleva).
    if($objednavka->{slevkod} =~ m/^(DES|ZAK)7121$/i && substr($objednavka->{cas}, 0, 8) < 20071202 && !$objednavka->{sleva_org_deti})
    {
        $objednavka->{sleva_kod} = 5;
    }
    # Získat z databáze přehled zboží.
    my @nazvy = qw(kod nazev prodejni_cena clenska_cena nelze_koupit sleva cerny_most);
    my $filtr = join(" OR ", map {"(kod = '$_->{kod}')"} @{$kosik});
    my $zdroj = $filtr ? "zbozi WHERE $filtr" : "zbozi";
    my $zbozidb = dzsql::dotaz($databaze, @nazvy, $zdroj);
    # Projít výsledek dotazu a přepsat údaje do košíku.
    # Sledovat, zda máme vše v dostatečném množství skladem na Černém Mostě.
    # Pokud ano, budeme moci zákazníkovi nabídnout expresní zaslání.
    my $skladem = 1;
    foreach my $z (@{$zbozidb})
    {
        # Přeskočit zboží, které nelze koupit. Protože se do košíku nedostane
        # název zboží, poznáme o něco níže, že toto zboží se má z košíku vyhodit.
        next if($z->{nelze_koupit});
        my $k = $kosik{$z->{kod}};
        foreach my $nazev (@nazvy)
        {
            $k->{$nazev} = $z->{$nazev};
        }
        # Zjistit cenu po slevě.
        $k->{jednotkova_cena} = $objednavka->{sleva_org_deti} ? $z->{clenska_cena} : $z->{prodejni_cena};
        $k->{cena_celkem} = $k->{pocet} * $k->{jednotkova_cena};
        if($objednavka->{sleva_kod} && !$z->{sleva})
        {
            $k->{cena_pred_slevou_kodem} = $k->{cena_celkem};
            $k->{cena_celkem} -= int($k->{cena_celkem} * $objednavka->{sleva_kod} / 100 + 0.5);
        }
        # Ověřit zásoby.
        $k->{cerny_most} = $z->{cerny_most};
        if($z->{cerny_most}<$k->{pocet})
        {
            $skladem = 0;
        }
    }
    # Vyházet z košíku neznámé zboží a zboží, které nelze koupit.
    # (O těchto chybách nebudeme uživatele informovat, jen bychom ho mátli.
    # Klidně mohl mít v košíku vyřazené zboží, aniž o tom věděl, pokud se sem
    # dostal přes zastaralý odkaz někde na síti, který v sobě kódoval obsah košíku.)
    @{$kosik} = grep {$_->{nazev} ne ""} @{$kosik};
    #--------------------------------------------------------------------------
    # Podle parametrů CGI zjistit nároky na slevy, stanovit výši poštovného a celkovou cenu.
    # Vše uložit do cílového hashe, na který jsme dostali odkaz ($objednavka).
    # Uložit do objednávky odkaz na dekódovaný košík.
    $objednavka->{kosik} = $kosik;
    $objednavka->{skladem_cerny_most} = $skladem;
    # Vypočítat celkovou cenu zboží v košíku.
    $objednavka->{celkem} = 0;
    foreach my $vec (@{$kosik})
    {
        $objednavka->{celkem} += $vec->{cena_celkem};
    }
    $objednavka->{mezisoucet} = $objednavka->{celkem};
    # Přičíst poštovné. Není-li znám způsob převzetí a placení, předpokládáme dobírku.
    $objednavka->{postovne} = 0;
    # Pokud není znám způsob odběru nebo pokud jde o osobní odběr, je poštovné nulové.
    if($objednavka->{odber} eq "dobirka") ###!!! dobirka je blbý název, u odběru to zahrnuje i předplatbu
    {
        for(my $i = 0; $i<=$#glob_postovne; $i++)
        {
            if($objednavka->{celkem}<=$glob_postovne[$i][0])
            {
                $objednavka->{postovne} = $objednavka->{platba} eq "převodem" ? $glob_postovne[$i][2] : $glob_postovne[$i][1];
                last;
            }
        }
        # Rozlišit případy, kdy je poštovné zdarma, protože zákazník si zboží vyzvedne osobně,
        # a kdy je zdarma, protože je to velký nákup, na který se vztahuje sleva.
        # (Jen ve druhém případě totiž budeme chtít zákazníka informovat, jaké výhody se mu dostalo.)
        if($objednavka->{postovne}==0)
        {
            $objednavka->{postovne_zdarma} = 1;
        }
        # Připočítat případný expresní příplatek (i u poštovného zdarma!).
        if($objednavka->{rychlost} eq "zrychleně")
        {
            $objednavka->{postovne} += $glob_priplatek_zrychlene;
        }
        elsif($objednavka->{rychlost} eq "expresně")
        {
            $objednavka->{postovne} += $glob_priplatek_expres;
        }
    }
    $objednavka->{celkem} += $objednavka->{postovne};
    # Započítat množstevní slevu.
    # Předpokládáme, že množstevní sleva se aplikuje při vyšším nákupu,
    # než při jakém se odpouští poštovné, takže se nemusíme bát, že bychom
    # výši slevy počítali i z poštovného.
    $objednavka->{mnozstevni_sleva} = $objednavka->{celkem}>=$glob_mnozstevni_sleva_nad ? int($objednavka->{celkem}*$glob_mnozstevni_sleva/100) : 0;
    $objednavka->{celkem} -= $objednavka->{mnozstevni_sleva};
    return $objednavka;
}



#------------------------------------------------------------------------------
# Vygeneruje HTML s obsahem nákupního košíku.
#------------------------------------------------------------------------------
sub html
{
    my $objednavka = shift; # odkaz na hash: vnořené pole hashů kosik: kod_zbozi nazev_zbozi jednotkova_cena pocet cena_celkem
    my $povolit_zmeny = shift; # mají se vygenerovat odkazy na přidávání/ubírání z košíku?
    my $html;
    # Pokud je košík neprázdný, vypsat obsah. Jinak jen informaci o prázdném košíku.
    if(scalar(@{$objednavka->{kosik}}))
    {
        # Projít košík a vypsat jeho obsah.
        # O některých úkonech předpokládáme, že k nim došlo v okamžiku, kdy se převáděl
        # obsah košíku z parametrů cgi do hashe objednavka:
        # - uspořádání pole zboží např. podle názvů zboží
        # - odstranění zboží, které nemáme v nabídce (v parametrech mohlo být např. kvůli nacachovaným stránkám u vyhledávačů)
        foreach my $vec (@{$objednavka->{kosik}})
        {
            $html .= "<p><b>$vec->{pocet} × $vec->{nazev}</b> (po $vec->{jednotkova_cena}&nbsp;Kč) = <b>$vec->{cena_celkem}&nbsp;Kč</b>\n";
            # Umožnit uživateli změnit v objednávce počet kusů každého zboží.
            if($povolit_zmeny)
            {
                # Dočasně zvýšit počet kusů dané věci, abychom získali cgi podobu košíku pro odkaz na zvýšení.
                $vec->{pocet}++;
                my $novy_kosik = zakodovat($objednavka->{kosik});
                $vec->{pocet}--;
                $html .= "<br>".odkazpar("[o 1 víc]", "", "kosik=$novy_kosik")."\n";
                # Dočasně snížit počet kusů dané věci, abychom získali cgi podobu košíku pro odkaz na snížení.
                # (Funkce zakodovat_kosik() sama ošetří případy, kdy počet klesne na nulu nebo pod ni.)
                $vec->{pocet}--;
                my $novy_kosik = zakodovat($objednavka->{kosik});
                $vec->{pocet}++;
                $html .= odkazpar("[o 1 míň]", "", "kosik=$novy_kosik")."<br>\n";
            }
        }
        #----------------------------------------------------------------------
        $html .= "<p><b>Celkem $objednavka->{mezisoucet}&nbsp;Kč</b>.<br>\n";
        # Přičíst poštovné.
        # Pokud není znám způsob odběru nebo pokud jde o osobní odběr, informaci o poštovném nezobrazovat.
        if($objednavka->{odber} eq "dobirka") ###!!! dobirka je blbý název, u odběru to zahrnuje i předplatbu
        {
            if($objednavka->{postovne_zdarma})
            {
                $html .= "<p><font color=red>Poštovné a balné ZDARMA.</font></p>\n";
            }
            else
            {
                $html .= "<p>Poštovné a balné <b>$objednavka->{postovne}&nbsp;Kč.</b></p>\n";
            }
        }
        # Započítat množstevní slevu.
        # Předpokládáme, že množstevní sleva se aplikuje při vyšším nákupu,
        # než při jakém se odpouští poštovné, takže se nemusíme bát, že bychom
        # výši slevy počítali i z poštovného.
        if(exists($objednavka->{mnozstevni_sleva}) && $objednavka->{mnozstevni_sleva}>0)
        {
            $html .= "<p><font color=red>Množstevní sleva $glob_mnozstevni_sleva&nbsp;%<br>\n";
            $html .= "= $objednavka->{mnozstevni_sleva}&nbsp;Kč</font></p>\n";
        }
        # Závěr obsahu košíku.
        $html .= "<p><font color=red><b>Celková cena $objednavka->{celkem}&nbsp;Kč</b>.</font></b></p>\n";
        # Na cenu mělo vliv, zda jsme zákazníka považovali za organizaci pracující s mládeží. Proto to uvedeme hned pod košíkem.
        if($objednavka->{sleva_org_deti})
        {
            $html .= "<p><font color=red>Uvedené ceny zahrnují slevu pro organizace pracující s&nbsp;mládeží. Vaše IČO: $objednavka->{ico}</font></p>\n";
        }
        if($objednavka->{sleva_kod})
        {
            $html .= "<p><font color=red>S&nbsp;výjimkou akčního zboží ceny zahrnují slevu $objednavka->{sleva_kod}&nbsp;% na základě slevového kódu $objednavka->{slevkod}.</font></p>\n";
        }
    }
    else # košík je prázdný
    {
        $html .= "<p>Váš nákupní košík je zatím prázdný.</p>\n";
    }
    return $html;
}



#------------------------------------------------------------------------------
# Vygeneruje text do mailu s obsahem nákupního košíku.
#------------------------------------------------------------------------------
sub text
{
    my $objednavka = shift; # odkaz na hash: vnořené pole hashů kosik: kod_zbozi nazev_zbozi jednotkova_cena pocet cena_celkem
    my $mail;
    # Pokud je košík neprázdný, vypsat obsah. Jinak jen informaci o prázdném košíku.
    if(scalar(@{$objednavka->{kosik}}))
    {
        # Projít košík a vypsat jeho obsah.
        # O některých úkonech předpokládáme, že k nim došlo v okamžiku, kdy se převáděl
        # obsah košíku z parametrů cgi do hashe objednavka:
        # - uspořádání pole zboží např. podle názvů zboží
        # - odstranění zboží, které nemáme v nabídce (v parametrech mohlo být např. kvůli nacachovaným stránkám u vyhledávačů)
        foreach my $vec (@{$objednavka->{kosik}})
        {
            $mail .= sprintf("%2d × %4d Kč = %4d Kč: %s\n", $vec->{pocet}, $vec->{jednotkova_cena}, $vec->{cena_celkem}, $vec->{nazev});
        }
        #----------------------------------------------------------------------
        $mail .= sprintf("MEZISOUČET   = %4d Kč\n", $objednavka->{mezisoucet});
        # Přičíst poštovné.
        # Pokud není znám způsob odběru nebo pokud jde o osobní odběr, informaci o poštovném nezobrazovat.
        if($objednavka->{odber} eq "dobirka") ###!!! dobirka je blbý název, u odběru to zahrnuje i předplatbu
        {
            if($objednavka->{postovne_zdarma})
            {
                $mail .= sprintf("Poštovné     = %4d Kč\n", 0);
            }
            else
            {
                $mail .= sprintf("Poštovné     = %4d Kč\n", $objednavka->{postovne});
            }
        }
        # Započítat množstevní slevu.
        # Předpokládáme, že množstevní sleva se aplikuje při vyšším nákupu,
        # než při jakém se odpouští poštovné, takže se nemusíme bát, že bychom
        # výši slevy počítali i z poštovného.
        if(exists($objednavka->{mnozstevni_sleva}) && $objednavka->{mnozstevni_sleva}>0)
        {
            $mail .= sprintf("Sleva $glob_mnozstevni_sleva %%    = %4d Kč\n", $objednavka->{mnozstevni_sleva});
        }
        # Závěr obsahu košíku.
        $mail .= sprintf("CELKEM       = %4d Kč\n\n", $objednavka->{celkem});
        # Na cenu mělo vliv, zda jsme zákazníka považovali za organizaci pracující s mládeží. Proto to uvedeme hned pod košíkem.
        if($objednavka->{sleva_org_deti})
        {
            $mail .= "Sleva pro organizace pracující s mládeží byla započtena. (IČO: $objednavka->{ico})\n";
        }
        else
        {
            $mail .= "Není organizace pracující s mládeží.";
            if($objednavka->{ico} !~ m/^\s*$/)
            {
                $mail .= " IČO: $objednavka->{ico}";
            }
            $mail .= "\n";
        }
        if($objednavka->{sleva_kod})
        {
            $mail .= "S výjimkou akčního zboží ceny zahrnují slevu $objednavka->{sleva_kod} % na základě slevového kódu $objednavka->{slevkod}.\n";
        }
    }
    return $mail;
}



1;
