summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorMitch Jackson <mitch@freeside.biz>2018-01-11 20:05:34 -0600
committerMitch Jackson <mitch@freeside.biz>2018-01-11 20:05:34 -0600
commit69e481a4a9191b9912d6bb8202627a5dc75f74ce (patch)
tree85604b29a5ce712824472778f488e69d0b0be1c5 /FS
parent77daf007ef522ae71041d9b094643cf868d8ecce (diff)
rt# 74031 implement svc_realestate
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Mason.pm3
-rw-r--r--FS/FS/Schema.pm90
-rw-r--r--FS/FS/h_svc_realestate.pm31
-rw-r--r--FS/FS/realestate_location.pm177
-rw-r--r--FS/FS/realestate_unit.pm163
-rw-r--r--FS/FS/svc_Common.pm10
-rw-r--r--FS/FS/svc_realestate.pm172
-rw-r--r--FS/t/realestate_location.t5
-rw-r--r--FS/t/realestate_unit.t5
-rw-r--r--FS/t/svc_realestate.t5
10 files changed, 658 insertions, 3 deletions
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 7bdb6059e..7f883dec1 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -418,6 +418,9 @@ if ( -e $addl_handler_use_file ) {
use FS::part_svc_msgcat;
use FS::commission_schedule;
use FS::commission_rate;
+ use FS::realestate_location;
+ use FS::realestate_unit;
+ use FS::svc_realestate;
use FS::saved_search;
use FS::sector_coverage;
# Sammath Naur
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 6d7520bd9..f7ac973be 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -495,8 +495,44 @@ sub tables_hashref {
my $username_len = 64; #usernamemax config file
- # name type nullability length default local
+ # Return a hashref defining the entire application database schema
+ # Each key of the hashref contains a structure describing a database table
+ #
+ # table_name => {
+ # columns => [...],
+ # primary_key => 'column',
+ # unique => [column,column,...],
+ # index => [[column],[column,column],...],
+ # foreign_keys => [{...},{...},...],
+ # }
+ #
+ #
+ # columns => [
+ #
+ # 'column_name',
+ #
+ # 'column_type',
+ #
+ # 'NULL' or '', # 'NULL' : Allow null values
+ # # '' : Disallow null values
+ #
+ # 'length', # Column size value. eg:
+ # # 40 : VARCHAR(40)
+ # # '10,2' : FLOAT(10,2)
+ #
+ # 'default', # Default column value for a new record
+ # # (Unclear if setting this to '' results in a default
+ # # value of NULL or empty string?)
+ #
+ # '', # local ?
+ #
+ # name, type, nullability, length, default, local,
+ # name, type, nullability, length, default, local,
+ # ...
+ #
+ # ],
+ # name type nullability length default local
return {
'agent' => {
@@ -7601,6 +7637,57 @@ sub tables_hashref {
'foreign_keys' => [],
},
+ 'realestate_unit' => {
+ 'columns' => [
+ 'realestatenum', 'serial', '', '', '', '',
+ 'realestatelocnum', 'int', '', '', '', '',
+ 'agentnum', 'int', 'NULL', '', '', '',
+ 'unit_title', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'realestatenum',
+ 'unique' => [ ['unit_title'] ],
+ 'index' => [
+ ['agentnum'],
+ ['realestatelocnum'],
+ ['disabled'],
+ ['unit_title'],
+ ],
+ 'foreign_keys' => [
+ {columns => ['agentnum'], table => 'agent'},
+ {columns => ['realestatelocnum'] => table => 'realestate_location'},
+ ],
+ },
+
+ realestate_location => {
+ 'columns' => [
+ '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 => 'realestatelocnum',
+ 'unique' => [ ['location_title'] ],
+ 'index' => [ ['agentnum'], ['disabled'] ],
+ 'foreign_keys' => [
+ {columns => ['agentnum'], table => 'agent'},
+ ],
+ },
+
+ svc_realestate => {
+ columns => [
+ 'svcnum', 'serial', '', '', '', '',
+ 'realestatenum', 'int', 'NULL', '', '', '',
+ ],
+ primary_key => 'svcnum',
+ index => [],
+ },
+
# name type nullability length default local
#'new_table' => {
@@ -7627,4 +7714,3 @@ L<DBIx::DBSchema>
=cut
1;
-
diff --git a/FS/FS/h_svc_realestate.pm b/FS/FS/h_svc_realestate.pm
new file mode 100644
index 000000000..2fdd291d1
--- /dev/null
+++ b/FS/FS/h_svc_realestate.pm
@@ -0,0 +1,31 @@
+package FS::h_svc_realestate;
+
+use strict;
+use vars qw( @ISA );
+use FS::h_Common;
+
+
+@ISA = qw( FS::h_Common );
+
+sub table { 'h_svc_realestate' };
+
+=head1 NAME
+
+FS::h_svc_circuit - Historical realestate service objects
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+An FS::h_svc_realestate object
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::h_Common>, L<FS::svc_realestate>, L<FS::Record>, schema.html from the base
+documentation.
+
+=cut
+
+1;
diff --git a/FS/FS/realestate_location.pm b/FS/FS/realestate_location.pm
new file mode 100644
index 000000000..d9cd76a58
--- /dev/null
+++ b/FS/FS/realestate_location.pm
@@ -0,0 +1,177 @@
+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,
+ location_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;
+
+=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_unit.pm b/FS/FS/realestate_unit.pm
new file mode 100644
index 000000000..d1d1f7fda
--- /dev/null
+++ b/FS/FS/realestate_unit.pm
@@ -0,0 +1,163 @@
+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 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
+
+=item custnum
+
+Pull the assigned custnum for this unit, if provisioned
+
+=cut
+
+sub custnum {
+ my $self = shift;
+ return $self->{custnum}
+ if $self->{custnum};
+
+ # select cust_pkg.custnum
+ # from svc_realestate
+ # LEFT JOIN cust_svc ON svc_realestate.svcnum = cust_svc.svcnum
+ # LEFT JOIN cust_pkg ON cust_svc.pkgnum = cust_pkg.pkgnum
+ # WHERE svc_realestate.realestatenum = $realestatenum
+
+ my $row = qsearchs({
+ select => 'cust_pkg.custnum',
+ table => 'svc_realestate',
+ addl_from => 'LEFT JOIN cust_svc ON svc_realestate.svcnum = cust_svc.svcnum '
+ . 'LEFT JOIN cust_pkg ON cust_svc.pkgnum = cust_pkg.pkgnum ',
+ extra_sql => 'WHERE svc_realestate.realestatenum = '.$self->realestatenum,
+ });
+
+ return
+ unless $row && $row->custnum;
+
+ return $self->{custnum} = $row->custnum;
+}
+
+=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/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index f2456a56f..afd5db64f 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -122,6 +122,15 @@ sub virtual_fields {
=item label
+Returns a label to identify a record of this service.
+Label may be displayed on freeside screens, and within customer bills.
+
+For example, $obj->label may return:
+
+ - A provisioned phone number for svc_phone
+ - The mailing list name and e-mail address for svc_mailinglist
+ - The address of a rental property svc_realestate
+
svc_Common provides a fallback label subroutine that just returns the svcnum.
=cut
@@ -1586,4 +1595,3 @@ from the base documentation.
=cut
1;
-
diff --git a/FS/FS/svc_realestate.pm b/FS/FS/svc_realestate.pm
new file mode 100644
index 000000000..a7512eef8
--- /dev/null
+++ b/FS/FS/svc_realestate.pm
@@ -0,0 +1,172 @@
+package FS::svc_realestate;
+use base qw(FS::svc_Common);
+
+use strict;
+use warnings;
+use vars qw($conf);
+
+use FS::Record qw(qsearchs qsearch dbh);
+use Tie::IxHash;
+
+$FS::UID::callback{'FS::svc_realestate'} = sub {
+ $conf = new FS::Conf;
+};
+
+=head1 NAME
+
+FS::svc_realestate - Object methods for svc_realestate records
+
+=head1 SYNOPSIS
+
+ {...} TODO
+
+=head1 DESCRIPTION
+
+A FS::svc_realestate object represents a billable real estate trasnaction,
+such as renting a home or office.
+
+FS::svc_realestate inherits from FS::svc_Common. The following fields are
+currently supported:
+
+=over 4
+
+=item svcnum - primary key
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Instantiates a new svc_realestate object.
+
+=cut
+
+sub table_info {
+ tie my %fields, 'Tie::IxHash',
+ svcnum => 'Service',
+ realestatenum => {
+ type => 'select-realestate_unit',
+ label => 'Real estate unit',
+ };
+
+ {
+ name => 'Real estate',
+ name_plural => 'Real estate services',
+ longname_plural => 'Real estate services',
+ display_weight => 100,
+ cancel_weight => 100,
+ fields => \%fields,
+ };
+}
+
+sub table {'svc_realestate'}
+
+=item label
+
+Returns a label formatted as:
+ <location_title> <unit_title>
+
+=cut
+
+sub label {
+ my $self = shift;
+ my $unit = $self->realestate_unit;
+ my $location = $self->realestate_location;
+
+ return $location->location_title.' '.$unit->unit_title
+ if $unit && $location;
+
+ return $self->svcnum; # shouldn't happen
+}
+
+
+=item realestate_unit
+
+Returns associated L<FS::realestate_unit>
+
+=cut
+
+sub realestate_unit {
+ my $self = shift;
+
+ return $self->get('_realestate_unit')
+ if $self->get('_realestate_unit');
+
+ return unless $self->realestatenum;
+
+ my $realestate_unit = qsearchs(
+ 'realestate_unit',
+ {realestatenum => $self->realestatenum}
+ );
+
+ $self->set('_realestate_unit', $realestate_unit);
+ $realestate_unit;
+}
+
+=item realestate_location
+
+Returns associated L<FS::realestate_location>
+
+=cut
+
+sub realestate_location {
+ my $self = shift;
+
+ my $realestate_unit = $self->realestate_unit;
+ return unless $realestate_unit;
+
+ $realestate_unit->location;
+}
+
+=item cust_svc
+
+Returns associated L<FS::cust_svc>
+
+=cut
+
+sub cust_svc {
+ qsearchs('cust_svc', { 'svcnum' => $_[0]->svcnum } );
+}
+
+=item search_sql
+
+I have an unfounded suspicion this method serves no purpose in this context
+
+=cut
+
+# sub search_sql {die "search_sql called on FS::svc_realestate"}
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=back 4
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
diff --git a/FS/t/realestate_location.t b/FS/t/realestate_location.t
new file mode 100644
index 000000000..ecb1d8be9
--- /dev/null
+++ b/FS/t/realestate_location.t
@@ -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_unit.t b/FS/t/realestate_unit.t
new file mode 100644
index 000000000..bbecc1a4c
--- /dev/null
+++ b/FS/t/realestate_unit.t
@@ -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";
diff --git a/FS/t/svc_realestate.t b/FS/t/svc_realestate.t
new file mode 100644
index 000000000..4145d8b52
--- /dev/null
+++ b/FS/t/svc_realestate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_realestate;
+$loaded=1;
+print "ok 1\n";