# mangled by kyle

use strict;
use warnings;
use 5.10.1;

no warnings 'redefine'; # 'cause the whole purpose of this file is to redefine Create

package RT::Asset;


=head1 NAME

RT::Asset - Represents a single asset record

=cut

=head1 DESCRIPTION

An Asset is a small record object upon which zero to many custom fields are
applied.  The core fields are:

=over 4

=item id

=item Name

Limited to 255 characters.

=item Description

Limited to 255 characters.

=item Catalog

=item Status

=item Creator

=item Created

=item LastUpdatedBy

=item LastUpdated

=back

All of these are readable through methods of the same name and mutable through
methods of the same name with C<Set> prefixed.  The last four are automatically
managed.

=head1 METHODS

=head2 Create PARAMHASH

Create takes a hash of values and creates a row in the database.  Available keys are:

=over 4

=item Name

=item Description

=item Catalog

Name or numeric ID

=item CustomField-<ID>

Sets the value for this asset of the custom field specified by C<< <ID> >>.

C<< <ID> >> should be a numeric ID, but may also be a Name if and only if your
custom fields have unique names.  Without unique names, the behaviour is
undefined.

=item Status

=item Owner, HeldBy, Contact

A single principal ID or array ref of principal IDs to add as members of the
respective role groups for the new asset.

User Names and EmailAddresses may also be used, but Groups must be referenced
by ID.

=item RefersTo, ReferredToBy, DependsOn, DependedOnBy, Parents, Children, and aliases

Any of these link types accept either a single value or arrayref of values
parseable by L<RT::URI>.

=back

Returns a tuple of (status, msg) on failure and (id, msg, non-fatal errors) on
success, where the third value is an array reference of errors that occurred
but didn't prevent creation.

=cut

sub Create {
    my $self = shift;
    my %args = (
        Name            => '',
        Description     => '',
        Catalog         => undef,

        Owner           => undef,
        HeldBy          => undef,
        Contact         => undef,

        Status          => undef,
        @_
    );
    my @non_fatal_errors;

    return (0, $self->loc("Invalid Catalog"))
        unless $self->ValidateCatalog( $args{'Catalog'} );

    my $catalog = RT::Catalog->new( $self->CurrentUser );
    $catalog->Load($args{'Catalog'});

    $args{'Catalog'} = $catalog->id;

    return (0, $self->loc("Permission Denied"))
        unless $catalog->CurrentUserHasRight('CreateAsset');

    return (0, $self->loc('Invalid Name (names may not be all digits)'))
        unless $self->ValidateName( $args{'Name'} );

    # Begin kyle's insertion
    # Check for validity and uniqueness of any given COE Number; if none is given choose the next available
    # number from the database
    my $InputCOENumber = $args{'CustomField-1'}[0];
    my ( $coeNumber, $coeNumberMessages ) = $self->_kdd_ValidateCOE( $InputCOENumber );
    return (0, $self->loc($coeNumberMessages))
    	unless $coeNumber;
    $args{'CustomField-1'} = $coeNumber;
    # End kyle's insertion

    # XXX TODO: This status/lifecycle pattern is duplicated in RT::Ticket and
    # should be refactored into a role helper.
    my $cycle = $catalog->LifecycleObj;
    unless ( defined $args{'Status'} && length $args{'Status'} ) {
        $args{'Status'} = $cycle->DefaultOnCreate;
    }

    $args{'Status'} = lc $args{'Status'};
    unless ( $cycle->IsValid( $args{'Status'} ) ) {
        return ( 0,
            $self->loc("Status '[_1]' isn't a valid status for assets.",
                $self->loc($args{'Status'}))
        );
    }

    unless ( $cycle->IsTransition( '' => $args{'Status'} ) ) {
        return ( 0,
            $self->loc("New assets cannot have status '[_1]'.",
                $self->loc($args{'Status'}))
        );
    }

    my $roles = {};
    my @errors = $self->_ResolveRoles( $roles, %args );
    return (0, @errors) if @errors;

    RT->DatabaseHandle->BeginTransaction();

    my ( $id, $msg ) = $self->SUPER::Create(
        map { $_ => $args{$_} } grep {exists $args{$_}}
            qw(id Name Description Catalog Status),
    );
    unless ($id) {
        RT->DatabaseHandle->Rollback();
        return (0, $self->loc("Asset create failed: [_1]", $msg));
    }

    # Let users who just created an asset see it until the end of this method.
    $self->{_object_is_readable} = 1;

    # Create role groups
    unless ($self->_CreateRoleGroups()) {
        RT->Logger->error("Couldn't create role groups for asset ". $self->id);
        RT->DatabaseHandle->Rollback();
        return (0, $self->loc("Couldn't create role groups for asset"));
    }

    # Figure out users for roles
    push @non_fatal_errors, $self->_AddRolesOnCreate( $roles, map { $_ => sub {1} } $self->Roles );

    # Add CFs
    foreach my $key (keys %args) {
        next unless $key =~ /^CustomField-(.+)$/i;
        my $cf   = $1;
        my @vals = ref $args{$key} eq 'ARRAY' ? @{ $args{$key} } : $args{$key};
        foreach my $value (@vals) {
            next unless defined $value;

            my ( $cfid, $cfmsg ) = $self->AddCustomFieldValue(
                (ref($value) eq 'HASH'
                    ? %$value
                    : (Value => $value)),
                Field             => $cf,
                RecordTransaction => 0
            );
            unless ($cfid) {
                RT->DatabaseHandle->Rollback();
                return (0, $self->loc("Couldn't add custom field value on create: [_1]", $cfmsg));
            }
        }
    }

    # Create transaction
    my ( $txn_id, $txn_msg, $txn ) = $self->_NewTransaction( Type => 'Create' );
    unless ($txn_id) {
        RT->DatabaseHandle->Rollback();
        return (0, $self->loc( 'Asset Create txn failed: [_1]', $txn_msg ));
    }

    # Add links
    push @non_fatal_errors, $self->_AddLinksOnCreate(\%args);

    RT->DatabaseHandle->Commit();

    # Let normal ACLs take over.
    delete $self->{_object_is_readable};

    return ($id, $self->loc('Asset #[_1] created: [_2]', $self->id, $args{'Name'}), \@non_fatal_errors);
}

=head1 MY METHODS

=head2 _kdd_IsCOEUnique

Is the given COE Number unique in the database?

=cut

sub _kdd_IsCOEUnique {

    my $self = shift;
    my $InputCOENumber = shift;
    my $COEIsUnique = 0;

    my $query = "select count(*) from ObjectCustomFieldValues where CustomField='1' and Content=$InputCOENumber";
    $COEIsUnique = !(RT->DatabaseHandle->FetchResult($query));

    return ($COEIsUnique);

}

=head2 _kdd_ValidateCOE

On success, return a valid COE Number and, optionally, a message.

On failure, return '0' and one or more reasons why.

Allowable input COE Numbers:

=over 4

=item Nothing

This will trigger a query to the database to find the next available number.

=item A positive integer, n.

If n is not already in the database, accept it.

If n is already in the database, reject it.

=back

On any other input, return failure.

=cut

sub _kdd_ValidateCOE {

    my $self = shift;
    my $coeNumber = shift;

    my $messages = '';

    # if no number is given get the next available one
    if (!defined $coeNumber) {
        my $coeNumber = $self->_kdd_getNextCOENumber;
        return ($coeNumber, $messages);
    }

    # check that the number is just a number
    # if it isn't we don't want to even bother the db.
    unless ($coeNumber =~ /^\d+$/) {
	return (0, 'COE Number must be a single positive integer. ');
    }

    unless ($self->_kdd_IsCOEUnique( $coeNumber )) {
        # if a number is specified and is already in use, try again
        $messages .= "COE Number $coeNumber is already in use.  Please choose a different one or leave the field blank to have one assigned. ";
    }

    if (length $messages) {
	# $messages mean something went wrong, try again
        return (0, $messages);
    } else {
	# no messages, accept the number
        return ($coeNumber, $messages);
    }

}

=head2 _kdd_getNextCOENumber

Return the next available COE number.

We kind of assume that, since COE Number is kind of our most important field,
it will be Custom Field #1.  Adjust the query if that's not the case.

=cut

sub _kdd_getNextCOENumber {

	my $query = "select max(cast(Content as signed))+1 from ObjectCustomFieldValues where CustomField = (select id from CustomFields where Name = 'CoE number')";
	my $nextCoENumber = RT->DatabaseHandle->FetchResult($query);
	return $nextCoENumber;

}

#RT::Base->_ImportOverlays();

1;
