Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / FS / cust_msg.pm
1 package FS::cust_msg;
2
3 use strict;
4 use base qw( FS::cust_main_Mixin FS::Record );
5 use FS::Record qw( qsearch qsearchs );
6 use MIME::Parser;
7 use vars qw( @statuses );
8
9 =head1 NAME
10
11 FS::cust_msg - Object methods for cust_msg records
12
13 =head1 SYNOPSIS
14
15   use FS::cust_msg;
16
17   $record = new FS::cust_msg \%hash;
18   $record = new FS::cust_msg { 'column' => 'value' };
19
20   $error = $record->insert;
21
22   $error = $record->check;
23
24 =head1 DESCRIPTION
25
26 An FS::cust_msg object represents an email message generated by Freeside 
27 and sent to a customer (see L<FS::msg_template>).  FS::cust_msg inherits 
28 from FS::Record.  The following fields are currently supported:
29
30 =over 4
31
32 =item custmsgnum - primary key
33
34 =item custnum - customer number
35
36 =item msgnum - template number
37
38 =item msgtype - the message type
39
40 =item _date - the time the message was sent
41
42 =item env_from - envelope From address
43
44 =item env_to - envelope To addresses, including Bcc, separated by newlines
45
46 =item header - message header
47
48 =item body - message body (as a complete MIME document)
49
50 =item error - Email::Sender error message (or null for success)
51
52 =back
53
54 =head1 METHODS
55
56 =over 4
57
58 =item new HASHREF
59
60 Creates a new 
61
62 =cut
63
64 # the new method can be inherited from FS::Record, if a table method is defined
65
66 sub table { 'cust_msg'; }
67
68 sub nohistory_fields { ('header', 'body'); } 
69 # history is kind of pointless on this table
70
71 @statuses = qw( prepared sent failed );
72
73 =item insert
74
75 Adds this record to the database.  If there is an error, returns the error 
76 and emits a warning; otherwise returns false.
77
78 =cut
79
80 sub insert {
81   # warn of all errors here; failing to insert/update one of these should 
82   # cause a warning at worst
83   my $self = shift;
84   my $error = $self->SUPER::insert;
85   warn "[cust_msg] error logging message status: $error\n" if $error;
86   return $error;
87 }
88
89 =item delete
90
91 Delete this record from the database.  There's no reason to do this.
92
93 =cut
94
95 sub delete {
96   my $self = shift;
97   warn "[cust_msg] log entry deleted\n";
98   return $self->SUPER::delete;
99 }
100
101 =item replace OLD_RECORD
102
103 Replaces the OLD_RECORD with this one in the database.  If there is an error,
104 returns the error and emits a warning, otherwise returns false.
105
106 =cut
107
108 sub replace {
109   my $self = shift;
110   my $error = $self->SUPER::replace(@_);
111   warn "[cust_msg] error logging message status: $error\n" if $error;
112   return $error;
113 }
114
115 =item check
116
117 Checks all fields to make sure this is a valid example.  If there is
118 an error, returns the error, otherwise returns false.  Called by the insert
119 and replace methods.
120
121 =cut
122
123 # the check method should currently be supplied - FS::Record contains some
124 # data checking routines
125
126 sub check {
127   my $self = shift;
128
129   my $error = 
130     $self->ut_numbern('custmsgnum')
131     || $self->ut_numbern('custnum')
132     || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum')
133     || $self->ut_numbern('msgnum')
134     || $self->ut_foreign_keyn('msgnum', 'msg_template', 'msgnum')
135     || $self->ut_numbern('_date')
136     || $self->ut_textn('env_from')
137     || $self->ut_textn('env_to')
138     || $self->ut_anything('header')
139     || $self->ut_anything('body')
140     || $self->ut_enum('status', \@statuses)
141     || $self->ut_textn('error')
142     || $self->ut_enum('msgtype', [  '',
143                                     'invoice',
144                                     'receipt',
145                                     'admin',
146                                  ])
147   ;
148   return $error if $error;
149
150   $self->SUPER::check;
151 }
152
153 =item send
154
155 Sends the message through its parent L<FS::msg_template>. Returns an error
156 message on error, or an empty string.
157
158 =cut
159
160 sub send {
161   my $self = shift;
162   my $msg_template = $self->msg_template
163     or return 'message was created without a template object';
164   $msg_template->send_prepared($self);
165 }
166
167 =item entity
168
169 Returns the complete message as a L<MIME::Entity>.
170
171 XXX this only works if the message in fact contains a MIME entity. Messages
172 created by external APIs may not look like that.
173
174 =item parts
175
176 Returns a list of the MIME parts contained in the message, as L<MIME::Entity>
177 objects.
178
179 =cut
180
181 sub entity {
182   my $self = shift;
183   if ( !exists($self->{entity}) ) {
184     my $parser = MIME::Parser->new;
185     my $output_dir = "$FS::UID::cache_dir/cache.$FS::UID::datasrc/mimeparts";
186     mkdir($output_dir) unless -d $output_dir;
187     $parser->output_under($output_dir);
188     $self->{entity} =
189       $parser->parse_data( $self->header . "\n" . $self->body );
190   }
191   $self->{entity};
192 }
193
194 sub parts {
195   my $self = shift;
196   # return only the parts with bodies, not the multipart containers
197   grep { $_->bodyhandle } $self->entity->parts_DFS;
198 }
199
200 =back
201
202 =head1 SEE ALSO
203
204 L<FS::msg_template>, L<FS::cust_main>, L<FS::Record>.
205
206 =cut
207
208 1;
209