package LATMOS::Accounts::Bases::Sql::Employment;

use 5.010000;
use strict;
use warnings;

use base qw(LATMOS::Accounts::Bases::Sql::objects);
use LATMOS::Accounts::Log;
use LATMOS::Accounts::I18N;
use Date::Parse;
use DateTime;
use Date::Calc;

our $VERSION = (q$Rev: 594 $ =~ /^Rev: (\d+) /)[0];

=head1 NAME

LATMOS::Accounts::Bases::Sql::Address - Physical office Adress Support

=head1 DESCRIPTION

C<Address> objects allow to store user's several office addresses.

Notice most of other bases (Ldap, ...) support only one address.

=cut

sub stringify {
    my ($self) = @_;

    my $user = $self->base->get_object('user', $self->_get_c_field('user'))
        or return $self->id;

    return join(', ', grep { $_ }
        (
            $user,
            $self->get_c_field('firstday'),
        )
    );
}

sub _object_table { 'employment' }

sub _key_field { 'name' }

sub _has_extended_attributes { 1 }

sub _get_attr_schema {
    my ($class, $base) = @_;

    my $attrs = {
        name =>         { inline => 1, },
        exported =>     { inline => 1, },
        user =>         { inline => 1,
            reference => 'user',
            label => l('User'),
        },
        description => {
            label => l('Description'),
        },
        firstday => {
            inline => 1,
            formtype => 'DATE',
            monitored => 1,
            label => l('Contract start'),
        },
        lastday => {
            inline => 1,
            formtype => 'DATE',
            monitored => 1,
            label => l('Contract end'),
        },
        length => {
            ro => 1,
            managed => 1,
            get => sub {
                my ($self) = @_;
                my $lastday = $self->object->get_attributes('lastday') || DateTime->now->ymd('-');
                my $firstday = $self->object->get_attributes('firstday');

                my @fd = split('-', $firstday);
                my @ld = split('-', $lastday);

                return Date::Calc::Delta_Days(@fd, @ld) +1;
            },
            label => l('Length'),
        },
        lengthText => {
            ro => 1,
            managed => 1,
            get => sub {
                my ($self) = @_;
                my $lastday = $self->object->get_attributes('lastday')|| DateTime->now->ymd('-');
                {
                    my $dtlast = DateTime->from_epoch(epoch => str2time($lastday));
                    $dtlast->add(days => 1);
                    $lastday = $dtlast->ymd('-');
                }
                my $firstday = $self->object->get_attributes('firstday');

                return if ($firstday gt $lastday);

                my @fd = split('-', $firstday) or return;
                my @ld = split('-', $lastday)  or return;

                my ($Dy,$Dm,$Dd) = Date::Calc::N_Delta_YMD(@fd, @ld);
                return join(', ',
                    ($Dy ? l('%d years', $Dy)  : ()),
                    ($Dm ? l('%d months', $Dm) : ()),
                    ($Dd ? l('%d days', $Dd)   : ()),
                );
            },
            label => l('Length'),
        },
        'state' => {
            managed => 1,
            ro => 1,
            get => sub {
                my ($self) = @_;
                my $now = DateTime->now;
                if ($now->epoch < str2time($self->object->get_attributes('firstday'))) {
                    return 1;
                } elsif ( my $end = $self->object->get_attributes('lastday') ) {
                    my $eend = str2time($end) + 86400;
                    if ($now->epoch > $eend) {
                        return -1;
                    } else {
                        return 0;
                    }
                } else {
                    return 0;
                }
            },
            label => l('State'),
        },
        contratType => {
            reference => 'group',
            can_values => sub {
                $base->search_objects('group', 'sutype=contrattype')
            },
            monitored => 1,
            label => l('Type of contract'),
        },
        managerContact => {
            delayed => 1,
            can_values => sub {
                my %uniq = map { $_ => 1 } grep { $_ }
                ($_[1] ? $_[1]->get_attributes('managerContact') : ()),
                $base->search_objects('user', 'active=*');
                sort keys %uniq;
            },
            reference => 'user',
            label => l('Direct manager'),
        },
        department => {
            reference => 'group',
            can_values => sub {
                $base->search_objects('group', 'sutype=dpmt')
            },
            monitored => 1,
            label => l('Department'),
        },
        company => {
            label => l('Company'),
        },
        employer => {
            label => l('Employer'),
        },
        endcircuit    => {
            formtype => 'DATE',
            monitored => 1,
            label => l('End of entrance circuit'),
        },
        hosted => {
            formtype => 'CHECKBOX',
            label => l('Hosted'),
        },
        previous => {
            ro => 1,
            managed => 1,
            get => sub {
                my ($self) = @_;
                my $find = $self->object->base->db->prepare_cached(q{
                    SELECT name from employment where "user" = ?
                        and (lastday is not null and lastday < ?)
                        order by lastday desc
                });
                $find->execute(
                    $self->object->get_field('user'),
                    $self->object->get_field('firstday')
                );
                my $res = $find->fetchrow_hashref;
                $find->finish;
                return $res->{name};
            },
            label => l('Previous'),
        },
        next => {
            ro => 1,
            managed => 1,
            get => sub {
                my ($self) = @_;
                my $find = $self->object->base->db->prepare_cached(q{
                    SELECT name from employment where "user" = ?
                        and firstday > ?
                        order by firstday asc
                });
                $find->execute(
                    $self->object->get_field('user'),
                    $self->object->get_field('lastday')
                );
                my $res = $find->fetchrow_hashref;
                $find->finish;
                return $res->{name};
            },
            label => l('Next'),
        },
        minFirstDay => {
            ro => 1,
            hidden => 1,
            managed => 1,
            formtype => 'DATE',
            get => sub {
                my ($attr) = @_;
                my $self = $attr->object;

                my $find = $self->base->db->prepare_cached(q{
                    SELECT max(lastday) as lastday FROM employment where "user" = ?
                        and lastday is not NULL and lastday < ?
                    });

                my $first = $self->get_field('firstday');

                $find->execute(
                    $self->get_field('user'),
                    $first
                );
                my $res = $find->fetchrow_hashref;
                $find->finish;

                if ($res && $res->{lastday}) {
                    my $dt = DateTime->from_epoch( epoch => str2time($res->{lastday}));
                    $dt->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
                    $dt->add(days => 1);
                    if ($first eq $dt->ymd('-')) {
                        return $first;
                    } else {
                        return $dt->ymd('-');
                    }
                } else {
                    return;
                }
            },
            label => l('Minimal first day'),
        },
        maxLastDay => {
            ro => 1,
            hidden => 1,
            managed => 1,
            formtype => 'DATE',
            get => sub {
                my ($attr) = @_;
                my $self = $attr->object;

                my $find = $self->base->db->prepare_cached(q{
                    SELECT min(firstday) as firstday FROM employment where "user" = ?
                        and firstday > ?
                    });

                my $last = $self->get_field('lastday') or do {
                    # This contract is last then...
                    return;
                };

                $find->execute(
                    $self->get_field('user'),
                    $last
                );
                my $res = $find->fetchrow_hashref;
                $find->finish;

                if ($res && $res->{firstday}) {
                    my $dt = DateTime->from_epoch( epoch => str2time($res->{firstday}));
                    $dt->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
                    $dt->subtract(days => 1);
                    if ($last eq $dt->ymd('-')) {
                        return $last;
                    } else {
                        return $dt->ymd('-');
                    }
                } else {
                    return;
                }
            },
            label => l('Maximal last day'),
        },
    };

    if (! $base->config("allow_pasted_employment")) {
    # Completed contract are RO, we allow to still set lastday
        foreach (qw(endcircuit firstday contratType department managerContact company employer)) {
            $attrs->{$_}{ro} = sub {
                my ($self) = $_[0];
                $self or return 0;
                my $st = $self->get_attributes('state') || '0';
                return $st < 0 ? 1 : 0;
            };
        }
    }
    $class->SUPER::_get_attr_schema($base, $attrs);
}

sub _create {
    my ($class, $base, $id, %data) = @_;
    $data{user} or return;
    my $user = $base->get_object('user', $data{user});
    $user or return;
    my $res = $class->SUPER::_create($base, $id, %data);
    $user->applyCurrentEmployment;
    $res;
}

sub _delete {
    my ($class, $base, $id) = @_;

    my $obj = $base->get_object($class->type, $id)
        or return;

    my $user = $obj->_get_attributes('user');

    my $res = $class->SUPER::_delete($base, $id);
    if ($res) {
        my $ouser = $base->get_object('user', $user);
        $ouser->applyCurrentEmployment;
    }

    $res
}

sub set_fields {
    my ($self, %data) = @_;

    my $res = $self->SUPER::set_fields(%data);

    my $user = $self->base->get_object('user', $self->get_attributes('user')) or do {
        $self->base->log(LA_ERR, "Cannot fetch user %s to apply employment", $self->get_attributes('user'));
        return;
    };

    $user->applyCurrentEmployment;

    return $res;
}

sub checkValues {
    my ($class, $base, $obj, %changes) = @_;

    my $user = $changes{user} || $obj->get_attributes('user');
    my $id = ref $obj ? $obj->id : $obj;

    my $firstday = exists($changes{firstday}) 
        ? $changes{firstday} : 
            ref $obj ? $obj->get_attributes('firstday') : undef;
    my $lastday  = exists($changes{lastday})
        ?  $changes{lastday} :
            ref $obj ? $obj->get_attributes('lastday') : undef;

    if ($lastday) {
        my $sth = $base->db->prepare_cached(q{
            select name, firstday, lastday from employment where "user" = ? and name != ?
                and
            (
            (firstday <= ? and (lastday is NULL or lastday >= ?))
                or
            ((lastday is NOT NULL and lastday <= ?) and firstday >= ?)
            )
        });
        $sth->execute($user, $id, $lastday, $firstday, $lastday, $firstday);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;

        if ($res) {
            $base->log(LA_ERR, "The change will overlap contrat %s (%s - %s)", $res->{name}, $res->{firstday}, $res->{lastday} || '');
            return;
        }
    } elsif (!$firstday) {
        $base->log(LA_ERR, "No firstday given");
        return;
    } else {
        my $sth = $base->db->prepare_cached(q{
            select * from employment where "user" = ? and name != ?
            and (lastday is NULL OR lastday >= ?)
            limit 1
        });
        $sth->execute($user, $id, $firstday);
        my $res = $sth->fetchrow_hashref;
        if ($res && $res->{name}) {
            if ($id ne $res->{name}) {
                $base->log(LA_ERR, "Another contract has no ending (%s)", $res->{name} || '');
                return;
            }
        }
    }

    return 1;
}

1;

__END__

=head1 SEE ALSO

L<LATMOS::Accounts::Bases::Sql>

=head1 AUTHOR

Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.


=cut
