rt# 74031 reworked realestate schema as locations and units
authorMitch Jackson <mitch@freeside.biz>
Thu, 14 Dec 2017 00:13:01 +0000 (18:13 -0600)
committerMitch Jackson <mitch@freeside.biz>
Tue, 19 Dec 2017 23:10:53 +0000 (17:10 -0600)
 - Update schemas
 - Original approach discarded and rewritten as locations with units

FS/FS/Mason.pm
FS/FS/Schema.pm
FS/FS/realestate_location.pm [new file with mode: 0644]
FS/FS/realestate_property.pm [deleted file]
FS/FS/realestate_subproperty.pm [deleted file]
FS/FS/realestate_unit.pm [new file with mode: 0644]
FS/t/realestate_location.t [new file with mode: 0644]
FS/t/realestate_property.t [deleted file]
FS/t/realestate_subproperty.t [deleted file]
FS/t/realestate_unit.t [new file with mode: 0644]

index 7bdb605..068f5b6 100644 (file)
@@ -223,6 +223,8 @@ if ( -e $addl_handler_use_file ) {
   use FS::cdr_batch;
   use FS::inventory_class;
   use FS::inventory_item;
+  use FS::realestate_location;
+  use FS::realestate_unit;
   use FS::pkg_category;
   use FS::pkg_class;
   use FS::access_user;
index b94407b..e1301b6 100644 (file)
@@ -7636,38 +7636,48 @@ sub tables_hashref {
       'foreign_keys'  => [],
     },
 
-    'realestate_property' => {
+    'realestate_unit' => {
       'columns' => [
-        'propnum',  'serial',  '',     '',      '',  '',
-        'agentnum', 'int',     'NULL', '',      '',  '',
-        'title',    'varchar', '',     $char_d, '',  '',
-        'address1', 'varchar', 'NULL', $char_d, '',  '',
-        'address2', 'varchar', 'NULL', $char_d, '',  '',
-        'city',     'varchar', 'NULL', $char_d, '',  '',
-        'state',    'varchar', 'NULL', $char_d, '',  '',
-        'zip',      'char',    'NULL', 5,       '',  '',
-        'disabled', 'char',    '',     1,       '0', '',
+        'realestatenum',    'serial',  '',     '',      '',  '',
+        'realestatelocnum', 'int',     '',     '',      '',   '',
+        'agentnum',         'int',     'NULL', '',      '',  '',
+        'custnum',          'int',     'NULL', '',      '',  '',
+        'unit_title',       'varchar', '',     $char_d, '',  '',
+        'disabled',         'char',    'NULL', 1,       '',  '',
+      ],
+      'primary_key'  => 'realestatenum',
+      'unique'       => [ ['unit_title'] ],
+      'index'        => [
+        ['agentnum'],
+        ['custnum'],
+        ['realestatelocnum'],
+        ['disabled'],
+        ['title']
       ],
-      'primary_key'  => 'propnum',
-      'unique'       => [],
-      'index'        => [ ['agentnum'] ],
       'foreign_keys' => [
-        {columns => ['agentnum'], table => 'agent'}
+        {columns => ['agentnum'], table => 'agent'},
+        {columns => ['custnum'],  table => 'cust_main'},
+        {columns => ['realestatelocnum'] => table => 'realestate_location'},
       ],
     },
 
-    'realestate_subproperty' => {
+    realestate_location => {
       'columns' => [
-        'subpropnum', 'serial',  '', '',      '',  '',
-        'propnum',    'int',     '', '',      '',  '',
-        'subtitle',   'varchar', '', $char_d, '',  '',
-        'disabled',   'char',    '', 1,       '0', '',
+        'realestatelocnum', 'serial',  '',     '',      '', '',
+        'agentnum',         'int',     'NULL', '',      '', '',
+        'location_title',   'varchar', '',     $char_d, '', '',
+        'address1',         'varchar', 'NULL', $char_d, '',  '',
+        'address2',         'varchar', 'NULL', $char_d, '',  '',
+        'city',             'varchar', 'NULL', $char_d, '',  '',
+        'state',            'varchar', 'NULL', $char_d, '',  '',
+        'zip',              'char',    'NULL', 5,       '',  '',
+        'disabled',         'char',    'NULL', 1,       '',  '',
       ],
-      'primary_key'  => 'subpropnum',
-      'unique'       => [],
-      'index'        => [ ['propnum'],['subtitle'] ],
+      primary_key  => 'realestatelocnum',
+      'unique'     => [ ['location_title'] ],
+      'index'      => [ ['agentnum'], ['disabled'] ],
       'foreign_keys' => [
-        {columns => ['propnum'], table => 'realestate_property'}
+        {columns => ['agentnum'], table => 'agent'},
       ],
     },
 
diff --git a/FS/FS/realestate_location.pm b/FS/FS/realestate_location.pm
new file mode 100644 (file)
index 0000000..08d6dd3
--- /dev/null
@@ -0,0 +1,178 @@
+package FS::realestate_location;
+use strict;
+use warnings;
+use Carp qw(croak);
+
+use base 'FS::Record';
+
+use FS::Record qw(qsearchs qsearch);
+
+=head1 NAME
+
+FS::realestate_location - Object representing a realestate_location record
+
+=head1 SYNOPSIS
+
+  use FS::realestate_location;
+
+  $location = new FS::realestate_location \%values;
+  $location = new FS::realestate_location {
+    agentnum => 1,
+    title    => 'Superdome',
+    address1 => '1500 Sugar Bowl Dr',
+    city     => 'New Orleans',
+    state    => 'LA',
+    zip      => '70112',
+  };
+
+  $error = $location->insert;
+  $error = $new_loc->replace($location);
+  $error = $record->check;
+
+  $error = $location->add_unit('Box Seat No. 42');
+  @units = $location->units;
+  @units = $location->active_units;
+
+=head1 DESCRIPTION
+
+An FS::realestate_location object represents a location for one or more
+FS::realestate_unit objects.  Expected to contain at least one unit, as only
+realestate_unit objects are assignable to packages via 
+L<FS::svc_realestate>.
+
+FS::realestate_location inherits from FS::Record.
+
+The following fields are currently supported:
+
+=over 4
+
+=item realestatelocnum
+
+=item agentnum
+
+=item location_title
+
+=item address1 (optional)
+
+=item address2 (optional)
+
+=item city (optional)
+
+=item state (optional)
+
+=item zip (optional)
+
+=item disabled
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF (see L<FS::Record>)
+
+=cut
+
+sub table {'realestate_location';}
+
+=item insert (see L<FS::Record>)
+
+=item delete
+
+  FS::realestate_location records should never be deleted, only disabled
+
+=cut
+
+sub delete {
+  # Once this record has been associated with a customer in any way, it
+  # should not be deleted.  todo perhaps, add a is_deletable function that
+  # checks if the record has ever actually been used, and allows deletion
+  # if it hasn't.  (entered in error, etc).
+  croak "FS::realestate_location records should never be deleted";
+}
+
+=item replace OLD_RECORD (see L<FS::Record>)
+
+=item check (see L<FS::Record>)
+
+=item agent
+
+Returns the associated agent
+
+=cut
+
+sub agent {
+  my $self = shift;
+  return undef unless $self->agentnum;
+  return exists $self->{agent}
+  ? $self->{agent}
+  : $self->{agent} = qsearchs('agent', {agentnum => $self->agentnum} );
+}
+
+
+=item add_unit UNIT_TITLE
+
+Create an associated L<FS::realestate_unit> record
+
+=cut
+
+sub add_unit {
+  my ($self, $unit_title) = @_;
+  croak "add_unit() requires a \$unit_title parameter" unless $unit_title;
+
+  if (
+    qsearchs('realestate_unit',{
+      realestatelocnum => $self->realestatelocnum,
+      unit_title => $unit_title,
+    })
+  ) {
+    return "Unit Title ($unit_title) has already been used for location (".
+      $self->location_title.")";
+  }
+
+  my $unit = FS::realestate_unit->new({
+    realestatelocnum => $self->realestatelocnum,
+    agentnum         => $self->agentnum,
+    unit_title       => $unit_title,
+  });
+  my $err = $unit->insert;
+  die "Error creating FS::realestate_new record: $err" if $err;
+
+  return;
+}
+
+
+=item units
+
+Returns all units associated with this location
+
+=cut
+
+sub units {
+  my $self = shift;
+  return qsearch(
+    'realestate_unit',
+    {realestatelocnum => $self->realestatelocnum}
+  );
+}
+
+
+=head1 SUBROUTINES
+
+=over 4
+
+=cut
+
+
+
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::record>, L<FS::realestate_unit>, L<FS::svc_realestate>
+
+=cut
+
+1;
diff --git a/FS/FS/realestate_property.pm b/FS/FS/realestate_property.pm
deleted file mode 100644 (file)
index 4e56417..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-package FS::realestate_property;
-use strict;
-use warnings;
-use Carp qw(croak);
-
-use base 'FS::Record';
-use FS::Record qw(qsearch qsearchs);
-
-=head1 NAME
-
-FS::realestate_property - Object representing a realestate_property record
-
-=head1 SYNOPSIS
-
-  use FS::realestate_property;
-
-  $record = new FS:realestate_property  \%values;
-  $record = new FS::realestate_property {
-    title    => 'Superdome',
-    address1 => '1500 Sugar Bowl Dr',
-    city     => 'New Orleans',
-    state    => 'LA',
-    zip      => '70112',
-    disabled => 0,
-    agentnum => 1,
-  };
-
-  $error = $record->insert;
-  $error = $new_rec->replace($record)
-  $error = $record->check;
-
-  $sub_record = $record->add_subproperty('Box Seat No. 42');
-
-  @subprops = $record->subproperties;
-  @subprops = $record->enabled_subproperties;
-
-=head1 DESCRIPTION
-
-An FS::realestate_property object represents a real estate property.  This
-object may represent a single property, such as a rental house.  It may also
-represent a group of properties sharing a common address or identifier, such
-as a shopping mall, apartment complex, or office building.
-
-FS::realestate_property inherits from FS::Record.
-
-The following fields are currently supported:
-
-=over 4
-
-=item propnum
-
-=item agentnum
-
-=item title
-
-=item address1 *optional
-
-=item address2 *optional
-
-=item city     *optional
-
-=item state    *optional
-
-=item zip      *optional
-
-=item disabled
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF (see L<FS::Record>)
-
-=cut
-
-sub table {'realestate_property';}
-
-=item insert (see L<FS::Record>)
-
-=item delete
-
-  FS::realestate_property records should never be deleted, only disabled
-
-=cut
-
-sub delete {
-  # Once this record has been associated with a customer in any way, it
-  # should not be deleted.  todo perhaps, add a is_deletable function that
-  # checks if the record has ever actually been used, and allows deletion
-  # if it hasn't.  (entered in error, etc).
-  croak "FS::realestate_property records should never be deleted";
-}
-
-
-=item replace OLD_RECORD (see L<FS::Record>)
-
-=item check (see L<FS::Record>)
-
-=item agent
-
-Returns the associated agent, if any, for this object
-
-=cut
-
-sub agent {
-  my $self = shift;
-  return undef unless $self->agentnum;
-  return qsearchs('agent', {agentnum => $self->agentnum} );
-}
-
-
-=item subproperties
-
-Returns all subproperties linked to this property
-
-=cut
-
-sub subproperties {
-  qsearch('realestate_subproperty', {propnum => shift->propnum} );
-}
-
-
-=item enabled_subproperties
-
-Returns all subproperties linked to this property where disabled = 0
-
-=cut
-
-sub enabled_subproperties {
-  qsearch( 'realestate_subproperty', {
-    propnum => shift->propnum,
-    disabled => 0,
-  });
-}
-
-=item add_subproperty SUBPROPERTY_TITLE
-
-Create a new subproperty record for this property
-
-=cut
-
-sub add_subproperty {
-  my ($self, $subtitle) = @_;
-  croak "add_subproperty() requires a \$subtitle parameter" unless $subtitle;
-
-  my $subp = new FS::realestate_subproperty {
-    propnum  => $self->propnum,
-    subtitle => $subtitle,
-    disabled => 0,
-  };
-  my $err = $subp->insert;
-  die "Error inserting subproperty: $err" if $err;
-  $subp;
-}
-
-
-=back
-
-=head1 SUBROUTINES
-
-=over 4
-
-=cut
-
-
-=back
-
-=head1 SEE ALSO
-
-L<FS::record>, L<FS::realestate_subproperty>, L<FS::svc_realestate>
-
-=cut
-
-1;
diff --git a/FS/FS/realestate_subproperty.pm b/FS/FS/realestate_subproperty.pm
deleted file mode 100644 (file)
index 5a93934..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-package FS::realestate_subproperty;
-use strict;
-use warnings;
-use Carp qw(croak);
-
-use base 'FS::Record';
-
-use FS::Record qw(qsearchs);
-
-=head1 NAME
-
-FS::realestate_subproperty - Object representing a realestate_subproperty record
-
-=head1 SYNOPSIS
-
-  use FS::realestate_subproperty;
-
-  $record = new FS::realestate_subproperty \%values;
-  $record = new FS::realestate_subproperty {
-    propnum => 65535,
-    subtitle => 'Box Seat No. 42',
-  };
-
-  $error = $record->insert;
-  $error = new_rec->replace($record);
-  $error = $record->check;
-
-  $parent = $record->property;
-
-=head1 DESCRIPTION
-
-An FS::realestate_subproperty object represents a unit of real estate property.
-Every L<FS::realestate_property> must contain at least one subproperty, or unit,
-which is the actual unit considered for sale, rent, etc as tied to
-L<FS::svc_realestate>.
-
-FS::realestate_subproperty inherits from FS::Record.
-
-The following fields are currently supported:
-
-=over 4
-
-=item subpropnum
-
-=item propnum
-
-=item subtitle
-
-=item disabled
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF (see L<FS::Record>)
-
-=cut
-
-sub table {'realestate_subproperty';}
-
-=item insert (see L<FS::Record>)
-
-=item delete
-
-  FS::realestate_subproperty records should never be deleted, only disabled
-
-=cut
-
-sub delete {
-  # Once this record has been associated with a customer in any way, it
-  # should not be deleted.  todo perhaps, add a is_deletable function that
-  # checks if the record has ever actually been used, and allows deletion
-  # if it hasn't.  (entered in error, etc).
-  croak "FS::realestate_subproperty records should never be deleted";
-}
-
-=item replace OLD_RECORD (see L<FS::Record>)
-
-=item check (see L<FS::Record>)
-
-=item agent
-
-Returns the associated agent, if any, for the parent L<FS::realestate_property>
-
-=cut
-
-sub agent {
-  shift->property->agent;
-}
-
-=item property
-
-Returns the associated parent L<FS::realestate_property> record
-
-=cut
-
-sub property {
-  my $self = shift;
-  exists $self->{property}
-  ? $self->{property}
-  : $self->{property} = qsearchs('realestate_property',$self->propnum);
-}
-
-=back
-
-=head1 SUBROUTINES
-
-=over 4
-
-=cut
-
-
-
-
-=back
-
-=head1 SEE ALSO
-
-L<FS::record>, L<FS::realestate_property>, L<FS::svc_realestate>
-
-=cut
-
-1;
diff --git a/FS/FS/realestate_unit.pm b/FS/FS/realestate_unit.pm
new file mode 100644 (file)
index 0000000..98b3337
--- /dev/null
@@ -0,0 +1,134 @@
+package FS::realestate_unit;
+use strict;
+use warnings;
+use Carp qw(croak);
+
+use base 'FS::Record';
+use FS::Record qw(qsearch qsearchs);
+
+=head1 NAME
+
+FS::realestate_unit - Object representing a realestate_unit record
+
+=head1 SYNOPSIS
+
+  use FS::realestate_unit;
+
+  $record = new FS:realestate_unit  \%values;
+  $record = new FS::realestate_unit {
+    realestatelocnum => 42,
+    agentnum         => 1,
+    unit_title       => 'Ste 404',
+  };
+
+  $error = $record->insert;
+  $error = $new_rec->replace($record)
+  $error = $record->check;
+
+  $location = $record->location;
+
+=head1 DESCRIPTION
+
+An FS::realestate_unit object represents an invoicable unit of real estate.
+Object may represent a single property, such as a rental house.  It may also
+represent a group of properties sharing a common address or identifier, such
+as a shopping mall, apartment complex, or office building, concert hall.
+
+A FS::realestate_unit object must be associated with a FS::realestate_location
+
+FS::realestate_unit inherits from FS::Record.
+
+The following fields are currently supported:
+
+=over 4
+
+=item realestatenum
+
+=item realestatelocnum
+
+=item agentnum
+
+=item custnum
+
+=item unit_title
+
+=item disabled
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF (see L<FS::Record>)
+
+=cut
+
+sub table {'realestate_unit';}
+
+=item insert (see L<FS::Record>)
+
+=item delete
+
+  FS::realestate_unit records should never be deleted, only disabled
+
+=cut
+
+sub delete {
+  # Once this record has been associated with a customer in any way, it
+  # should not be deleted.  todo perhaps, add a is_deletable function that
+  # checks if the record has ever actually been used, and allows deletion
+  # if it hasn't.  (entered in error, etc).
+  croak "FS::realestate_unit records should never be deleted";
+}
+
+
+=item replace OLD_RECORD (see L<FS::Record>)
+
+=item check (see L<FS::Record>)
+
+=item agent
+
+Returns the associated agent, if any, for this object
+
+=cut
+
+sub agent {
+  my $self = shift;
+  return undef unless $self->agentnum;
+  return qsearchs('agent', {agentnum => $self->agentnum} );
+}
+
+=item location
+
+  Return the associated FS::realestate_location object
+
+=cut
+
+sub location {
+  my $self = shift;
+  return $self->{location} if exists $self->{location};
+  return $self->{location} = qsearchs(
+    'realestate_location',
+    {realestatelocnum => $self->realestatelocnum}
+  );
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=cut
+
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::record>, L<FS::realestate_location>, L<FS::svc_realestate>
+
+=cut
+
+1;
diff --git a/FS/t/realestate_location.t b/FS/t/realestate_location.t
new file mode 100644 (file)
index 0000000..ecb1d8b
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::realestate_location;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/realestate_property.t b/FS/t/realestate_property.t
deleted file mode 100644 (file)
index af7f2f4..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::realestate_property;
-$loaded=1;
-print "ok 1\n";
diff --git a/FS/t/realestate_subproperty.t b/FS/t/realestate_subproperty.t
deleted file mode 100644 (file)
index 0f00eee..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::realestate_subproperty;
-$loaded=1;
-print "ok 1\n";
diff --git a/FS/t/realestate_unit.t b/FS/t/realestate_unit.t
new file mode 100644 (file)
index 0000000..bbecc1a
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::realestate_unit;
+$loaded=1;
+print "ok 1\n";