line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
1201
|
use utf8; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
12
|
|
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Interchange6::Schema::Result::Tax; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Interchange6::Schema::Result::Tax |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=cut |
10
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
87
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
45
|
|
12
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
62
|
|
13
|
2
|
|
|
2
|
|
14
|
use DateTime; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
64
|
|
14
|
2
|
|
|
2
|
|
11
|
use POSIX qw/ceil floor/; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
36
|
|
15
|
|
|
|
|
|
|
|
16
|
2
|
|
|
|
|
15
|
use Interchange6::Schema::Candy -components => [ |
17
|
|
|
|
|
|
|
qw(InflateColumn::DateTime TimeStamp |
18
|
|
|
|
|
|
|
+Interchange6::Schema::Component::Validation) |
19
|
2
|
|
|
2
|
|
250
|
]; |
|
2
|
|
|
|
|
7
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 DESCRIPTION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
The taxes table contains taxes such as sales tax and VAT. Each tax has a unique tax_name but can contain multiple rows for each tax_name to allow for changes in tax rates over time. When there is more than one row for a single tax_name then the valid_from and valid_to periods may not overlap. |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 ACCESSORS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head2 taxes_id |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Primary key. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=cut |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
primary_column taxes_id => { |
34
|
|
|
|
|
|
|
data_type => "integer", |
35
|
|
|
|
|
|
|
is_auto_increment => 1, |
36
|
|
|
|
|
|
|
sequence => "taxes_id_seq" |
37
|
|
|
|
|
|
|
}; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head2 tax_name |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Name of tax, e.g.: vat_full |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
column tax_name => { data_type => "varchar", size => 64 }; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head2 description |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
Description of tax, e.g.: New York sales tax |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=cut |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
column description => { data_type => "varchar", size => 64 }; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 percent |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Percent rate of tax, e.g.: 19.9775 |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=cut |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
column percent => |
62
|
|
|
|
|
|
|
{ data_type => "numeric", size => [ 7, 4 ] }; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 decimal_places |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Number of decimal_places of precision required for tax cost and reporting. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Defaults to 2. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=cut |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
column decimal_places => |
73
|
|
|
|
|
|
|
{ data_type => "integer", default_value => 2 }; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head2 rounding |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Default rounding is half round up to the number of decimal_places. To use floor or ceiling set rounding to 'f' or 'c' as appropriate. The rounding value is automatically converted to lower case and any invalid value passed in will cause an exception to be thrown. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Is nullable. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=cut |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
column rounding => |
84
|
|
|
|
|
|
|
{ data_type => "char", is_nullable => 1, size => 1 }; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head2 valid_from |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Date from which tax is valid. Defaults to time record is created. |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=cut |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
column valid_from => |
93
|
|
|
|
|
|
|
{ data_type => "date", set_on_create => 1 }; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 valid_to |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Final date on which tax is valid. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
Is nullable. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=cut |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
column valid_to => { data_type => "date", is_nullable => 1 }; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 country_iso_code |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
FK on L<Interchange6::Schema::Result::Country/country_iso_code>. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Is nullable. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=cut |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
column country_iso_code => |
114
|
|
|
|
|
|
|
{ data_type => "char", is_nullable => 1, size => 2 }; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=head2 states_id |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
FK on L<Interchange6::Schema::Result::State/states_id>. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Is nullable. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=cut |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
column states_id => |
125
|
|
|
|
|
|
|
{ data_type => "integer", is_nullable => 1 }; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head2 created |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Date and time when this record was created returned as L<DateTime> object. |
130
|
|
|
|
|
|
|
Value is auto-set on insert. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=cut |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
column created => |
135
|
|
|
|
|
|
|
{ data_type => "datetime", set_on_create => 1 }; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head2 last_modified |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Date and time when this record was last modified returned as L<DateTime> object. |
140
|
|
|
|
|
|
|
Value is auto-set on insert and update. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=cut |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
column last_modified => { |
145
|
|
|
|
|
|
|
data_type => "datetime", |
146
|
|
|
|
|
|
|
set_on_create => 1, |
147
|
|
|
|
|
|
|
set_on_update => 1, |
148
|
|
|
|
|
|
|
}; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 RELATIONS |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 state |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Type: belongs_to |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Related object: L<Interchange6::Schema::Result::State> |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=cut |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
belongs_to |
161
|
|
|
|
|
|
|
state => "Interchange6::Schema::Result::State", |
162
|
|
|
|
|
|
|
'states_id', |
163
|
|
|
|
|
|
|
{ |
164
|
|
|
|
|
|
|
is_deferrable => 1, |
165
|
|
|
|
|
|
|
on_delete => "CASCADE", |
166
|
|
|
|
|
|
|
on_update => "CASCADE", |
167
|
|
|
|
|
|
|
order_by => 'name', |
168
|
|
|
|
|
|
|
join_type => 'left', |
169
|
|
|
|
|
|
|
}; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 country |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Type: belongs_to |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Related object: L<Interchange6::Schema::Result::Country> |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
belongs_to |
180
|
|
|
|
|
|
|
country => "Interchange6::Schema::Result::Country", |
181
|
|
|
|
|
|
|
'country_iso_code', |
182
|
|
|
|
|
|
|
{ |
183
|
|
|
|
|
|
|
is_deferrable => 1, |
184
|
|
|
|
|
|
|
on_delete => "CASCADE", |
185
|
|
|
|
|
|
|
on_update => "CASCADE", |
186
|
|
|
|
|
|
|
order_by => 'name', |
187
|
|
|
|
|
|
|
join_type => 'left', |
188
|
|
|
|
|
|
|
}; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 METHODS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head2 calculate |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Calculate tax |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Arguments should be a hash ref of the following arguments: |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=over 4 |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=item * price |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Price of product either inclusive or exclusive of tax - required. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item * tax_included |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Boolean indicating whether price is inclusive of tax or not. Defaults to 0 which means exclusive of tax. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Will throw an exception if the price us not numeric. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=back |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Usage example: |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
my $tax = $taxrecord->caclulate({ price => 13.47, tax_included => 1 }); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# with percentage 18 our tax is 2.05 |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=cut |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub calculate { |
221
|
15
|
|
|
15
|
1
|
19912
|
my $self = shift; |
222
|
15
|
|
|
|
|
31
|
my $args = shift; |
223
|
|
|
|
|
|
|
|
224
|
15
|
|
|
|
|
57
|
my $schema = $self->result_source->schema; |
225
|
15
|
|
|
|
|
602
|
my $dtf = $schema->storage->datetime_parser; |
226
|
15
|
|
|
|
|
359
|
my $dt = DateTime->today; |
227
|
15
|
|
|
|
|
8724
|
my $tax; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
$schema->throw_exception("argument price is missing") |
230
|
15
|
100
|
|
|
|
70
|
unless defined $args->{price}; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
$schema->throw_exception( |
233
|
|
|
|
|
|
|
"argument price is not a valid numeric: " . $args->{price} ) |
234
|
13
|
100
|
|
|
|
172
|
unless $args->{price} =~ m/^(\d+)*(\.\d+)*$/; |
235
|
|
|
|
|
|
|
|
236
|
12
|
100
|
|
|
|
51
|
if ( $args->{tax_included} ) { |
237
|
1
|
|
|
|
|
31
|
my $nett = $args->{price} / ( 1 + ( $self->percent / 100 ) ); |
238
|
1
|
|
|
|
|
38
|
$tax = $args->{price} - $nett; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
else { |
241
|
11
|
|
|
|
|
322
|
$tax = $args->{price} * $self->percent / 100; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# round & return |
245
|
|
|
|
|
|
|
|
246
|
12
|
|
|
|
|
432
|
my $decimal_places = $self->decimal_places; |
247
|
|
|
|
|
|
|
|
248
|
12
|
100
|
|
|
|
360
|
unless ( $self->rounding ) { |
249
|
|
|
|
|
|
|
|
250
|
7
|
|
|
|
|
201
|
return sprintf( "%.${decimal_places}f", $tax ); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
else { |
253
|
|
|
|
|
|
|
|
254
|
5
|
|
|
|
|
73
|
$tax *= 10**$decimal_places; |
255
|
|
|
|
|
|
|
|
256
|
5
|
100
|
|
|
|
92
|
if ( $self->rounding eq 'c' ) { |
|
|
100
|
|
|
|
|
|
257
|
2
|
|
|
|
|
31
|
$tax = ceil($tax) / ( 10**$decimal_places ); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
elsif ( $self->rounding eq 'f' ) { |
260
|
2
|
|
|
|
|
89
|
$tax = floor($tax) / ( 10**$decimal_places ); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
else { |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# should not be possible to get here |
265
|
1
|
|
|
|
|
60
|
$schema->throw_exception( |
266
|
|
|
|
|
|
|
"rounding value from database is invalid: " . $self->rounding ); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
4
|
|
|
|
|
65
|
return sprintf( "%.${decimal_places}f", $tax ); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=head1 INHERITED METHODS |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head2 new |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
We overload the new method to set default values on certain rows at create time. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=cut |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub new { |
282
|
132
|
|
|
132
|
1
|
45199
|
my ( $class, $attrs ) = @_; |
283
|
|
|
|
|
|
|
|
284
|
132
|
|
|
|
|
591
|
my %attrs = %$attrs; |
285
|
|
|
|
|
|
|
|
286
|
132
|
100
|
|
|
|
613
|
$attrs->{decimal_places} = 2 unless defined $attrs->{decimal_places}; |
287
|
|
|
|
|
|
|
|
288
|
132
|
|
|
|
|
577
|
my $new = $class->next::method( \%attrs ); |
289
|
|
|
|
|
|
|
|
290
|
132
|
|
|
|
|
17874
|
return $new; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 sqlt_deploy_hook |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Called during table creation to add indexes on the following columns: |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=over 4 |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=item * tax_name |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=item * valid_from |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=item * valid_to |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=back |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=cut |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub sqlt_deploy_hook { |
310
|
1
|
|
|
1
|
1
|
1221474
|
my ( $self, $table ) = @_; |
311
|
|
|
|
|
|
|
|
312
|
1
|
|
|
|
|
8
|
$table->add_index( name => 'taxes_idx_tax_name', fields => ['tax_name'] ); |
313
|
1
|
|
|
|
|
822
|
$table->add_index( |
314
|
|
|
|
|
|
|
name => 'taxes_idx_valid_from', |
315
|
|
|
|
|
|
|
fields => ['valid_from'] |
316
|
|
|
|
|
|
|
); |
317
|
1
|
|
|
|
|
871
|
$table->add_index( |
318
|
|
|
|
|
|
|
name => 'taxes_idx_valid_to', |
319
|
|
|
|
|
|
|
fields => ['valid_to'] |
320
|
|
|
|
|
|
|
); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head2 validate |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Validity checks that cannot be enforced using primary key, unique or other database methods using L<Interchange6::Schema::Component::Validation>. The validity checks enforce the following rules: |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=over 4 |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item * Check country_iso_code is valid |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item * If both valid_from and valid_to are defined then valid_to must be a later date than valid_from. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item * A single tax_name may appear more than once in the table to allow for changes in tax rates but valid_from/valid_to date ranges must not overlap. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=back |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=cut |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub validate { |
340
|
135
|
|
|
135
|
1
|
324
|
my $self = shift; |
341
|
135
|
|
|
|
|
393
|
my $schema = $self->result_source->schema; |
342
|
135
|
|
|
|
|
4898
|
my $dtf = $schema->storage->datetime_parser; |
343
|
135
|
|
|
|
|
2807
|
my $rset; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# country iso code |
346
|
|
|
|
|
|
|
|
347
|
135
|
100
|
|
|
|
2816
|
if ( defined $self->country_iso_code ) { |
348
|
130
|
|
|
|
|
2222
|
$rset = |
349
|
|
|
|
|
|
|
$schema->resultset('Country') |
350
|
|
|
|
|
|
|
->search( { country_iso_code => $self->country_iso_code } ); |
351
|
130
|
100
|
|
|
|
80914
|
if ( $rset->count == 0 ) { |
352
|
1
|
|
|
|
|
3963
|
$schema->throw_exception( |
353
|
|
|
|
|
|
|
'country_iso_code not valid: ' . $self->country_iso_code ); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# rounding |
358
|
|
|
|
|
|
|
|
359
|
134
|
100
|
|
|
|
514698
|
if ( defined $self->rounding ) { |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# set lower case |
362
|
|
|
|
|
|
|
|
363
|
4
|
|
|
|
|
118
|
my $rounding = lc( $self->rounding ); |
364
|
4
|
|
|
|
|
116
|
$self->rounding($rounding); |
365
|
|
|
|
|
|
|
|
366
|
4
|
100
|
|
|
|
386
|
unless ( $self->rounding =~ /^(c|f)$/ ) { |
367
|
1
|
|
|
|
|
32
|
$self->rounding(undef); |
368
|
1
|
|
|
|
|
75
|
$schema->throw_exception( |
369
|
|
|
|
|
|
|
'value for rounding not c, f or undef: ' . $rounding ); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# check that valid_to is later than valid_from (if it is defined) |
374
|
|
|
|
|
|
|
|
375
|
133
|
|
|
|
|
6845
|
$self->valid_from->truncate( to => 'day' ); |
376
|
|
|
|
|
|
|
|
377
|
133
|
100
|
|
|
|
120184
|
if ( defined $self->valid_to ) { |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# remove time - we only want the date |
380
|
6
|
|
|
|
|
4169
|
$self->valid_to->truncate( to => 'day' ); |
381
|
|
|
|
|
|
|
|
382
|
6
|
100
|
|
|
|
1955
|
unless ( $self->valid_to > $self->valid_from ) { |
383
|
2
|
|
|
|
|
305
|
$schema->throw_exception("valid_to is not later than valid_from"); |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# grab our resultset |
388
|
|
|
|
|
|
|
|
389
|
131
|
|
|
|
|
8326
|
$rset = $self->result_source->resultset; |
390
|
|
|
|
|
|
|
|
391
|
131
|
100
|
|
|
|
46964
|
if ( $self->in_storage ) { |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# this is an update so we must exclude our existing record from |
394
|
|
|
|
|
|
|
# the resultset before range overlap checks are performed |
395
|
|
|
|
|
|
|
|
396
|
2
|
|
|
|
|
46
|
$rset = $rset->search( { taxes_id => { '!=', $self->taxes_id } } ); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# multiple entries for a single tax code do not overlap dates |
400
|
|
|
|
|
|
|
|
401
|
131
|
100
|
|
|
|
3815
|
if ( defined $self->valid_to ) { |
402
|
4
|
|
|
|
|
195
|
$rset = $rset->search( |
403
|
|
|
|
|
|
|
{ |
404
|
|
|
|
|
|
|
tax_name => $self->tax_name, |
405
|
|
|
|
|
|
|
-or => [ |
406
|
|
|
|
|
|
|
valid_from => { |
407
|
|
|
|
|
|
|
-between => [ |
408
|
|
|
|
|
|
|
$dtf->format_datetime( $self->valid_from ), |
409
|
|
|
|
|
|
|
$dtf->format_datetime( $self->valid_to ), |
410
|
|
|
|
|
|
|
] |
411
|
|
|
|
|
|
|
}, |
412
|
|
|
|
|
|
|
valid_to => { |
413
|
|
|
|
|
|
|
-between => [ |
414
|
|
|
|
|
|
|
$dtf->format_datetime( $self->valid_from ), |
415
|
|
|
|
|
|
|
$dtf->format_datetime( $self->valid_to ), |
416
|
|
|
|
|
|
|
] |
417
|
|
|
|
|
|
|
}, |
418
|
|
|
|
|
|
|
], |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
); |
421
|
|
|
|
|
|
|
|
422
|
4
|
100
|
|
|
|
2454
|
if ( $rset->count > 0 ) { |
423
|
3
|
|
|
|
|
16365
|
$schema->throw_exception( |
424
|
|
|
|
|
|
|
'tax overlaps existing date range: ' . $self->tax_name ); |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
else { |
428
|
127
|
|
|
|
|
6485
|
$rset = $rset->search( |
429
|
|
|
|
|
|
|
{ |
430
|
|
|
|
|
|
|
tax_name => $self->tax_name, |
431
|
|
|
|
|
|
|
-or => [ |
432
|
|
|
|
|
|
|
{ |
433
|
|
|
|
|
|
|
valid_to => undef, |
434
|
|
|
|
|
|
|
valid_from => |
435
|
|
|
|
|
|
|
{ '<=', $dtf->format_datetime( $self->valid_from ) }, |
436
|
|
|
|
|
|
|
}, |
437
|
|
|
|
|
|
|
{ |
438
|
|
|
|
|
|
|
valid_to => { '!=', undef }, |
439
|
|
|
|
|
|
|
valid_to => |
440
|
|
|
|
|
|
|
{ '>=', $dtf->format_datetime( $self->valid_from ) }, |
441
|
|
|
|
|
|
|
}, |
442
|
|
|
|
|
|
|
], |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
); |
445
|
|
|
|
|
|
|
} |
446
|
128
|
100
|
|
|
|
63618
|
if ( $rset->count > 0 ) { |
447
|
3
|
|
|
|
|
16147
|
$schema->throw_exception('tax overlaps existing date range'); |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
1; |