+ my $old = @_ ? shift : '';
+
+ my $error =
+ $self->ut_numbern('svcnum')
+ ;
+ return $error if $error;
+
+ my $part_svc = $self->part_svc;
+ return "Unkonwn svcpart" unless $part_svc;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ #set default/fixed/whatever fields from part_svc
+ my $table = $self->table;
+ foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
+
+ my $part_svc_column = $part_svc->part_svc_column($field);
+ my $columnflag = $part_svc_column->columnflag;
+ next unless $columnflag =~ /^[AM]$/;
+
+ next if $columnflag eq 'A' && $self->$field() ne '';
+
+ my $classnum = $part_svc_column->columnvalue;
+ my %hash;
+
+ if ( $columnflag eq 'A' && $self->$field() eq '' ) {
+ $hash{'svcnum'} = '';
+ } elsif ( $columnflag eq 'M' ) {
+ return "Select inventory item for $field" unless $self->getfield($field);
+ $hash{'item'} = $self->getfield($field);
+ my $chosen_classnum = $self->getfield($field.'_classnum');
+ if ( grep {$_ == $chosen_classnum} split(',', $classnum) ) {
+ $classnum = $chosen_classnum;
+ }
+ # otherwise the chosen classnum is either (all), or somehow not on
+ # the list, so ignore it and choose the first item that's in any
+ # class on the list
+ }
+
+ my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null' => 1,
+ 'table' => 'inventory_item',
+ );
+
+ my $inventory_item = qsearchs({
+ 'table' => 'inventory_item',
+ 'hashref' => \%hash,
+ 'extra_sql' => "AND classnum IN ($classnum) AND $agentnums_sql",
+ 'order_by' => 'ORDER BY ( agentnum IS NULL ) '. #agent inventory first
+ ' LIMIT 1 FOR UPDATE',
+ });
+
+ unless ( $inventory_item ) {
+ # should really only be shown if columnflag eq 'A'...
+ $dbh->rollback if $oldAutoCommit;
+ my $message = 'Out of ';
+ my @classnums = split(',', $classnum);
+ foreach ( @classnums ) {
+ my $class = FS::inventory_class->by_key($_)
+ or return "Can't find inventory_class.classnum $_";
+ $message .= PL_N($class->classname);
+ if ( scalar(@classnums) > 2 ) { # english is hard
+ if ( $_ != $classnums[-1] ) {
+ $message .= ', ';
+ }
+ }
+ if ( scalar(@classnums) > 1 and $_ == $classnums[-2] ) {
+ $message .= 'and ';
+ }
+ }
+ return $message;
+ }
+
+ next if $columnflag eq 'M' && $inventory_item->svcnum == $self->svcnum;
+
+ $self->setfield( $field, $inventory_item->item );
+ #if $columnflag eq 'A' && $self->$field() eq '';
+
+ # release the old inventory item, if there was one
+ if ( $old && $old->$field() && $old->$field() ne $self->$field() ) {
+ my $old_inv = qsearchs({
+ 'table' => 'inventory_item',
+ 'hashref' => {
+ 'svcnum' => $old->svcnum,
+ },
+ 'extra_sql' => "AND classnum IN ($classnum) AND ".
+ '( ( svc_field IS NOT NULL AND svc_field = '.$dbh->quote($field).' )'.
+ ' OR ( svc_field IS NULL AND item = '. dbh->quote($old->$field).' )'.
+ ')',
+ });
+ if ( $old_inv ) {
+ $old_inv->svcnum('');
+ $old_inv->svc_field('');
+ my $oerror = $old_inv->replace;
+ if ( $oerror ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error unprovisioning inventory: $oerror";
+ }
+ } else {
+ warn "old inventory_item not found for $field ". $self->$field;
+ }
+ }
+
+ $inventory_item->svcnum( $self->svcnum );
+ $inventory_item->svc_field( $field );
+ my $ierror = $inventory_item->replace();
+ if ( $ierror ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error provisioning inventory: $ierror";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+