]> git.imager.perl.org - bse.git/blame - site/cgi-bin/modules/BSE/TB/Product.pm
add some documentation
[bse.git] / site / cgi-bin / modules / BSE / TB / Product.pm
CommitLineData
10dd37f9 1package BSE::TB::Product;
15fb10f2 2use strict;
41b9d8ec 3# represents a product from the database
e0ed81d7 4use BSE::TB::Articles;
41b9d8ec 5use vars qw/@ISA/;
e0ed81d7 6@ISA = qw/BSE::TB::Article/;
41b9d8ec 7
9996f6d0 8our $VERSION = "1.008";
cb7fd78d 9
af74f0b4
TC
10# subscription_usage values
11use constant SUBUSAGE_START_ONLY => 1;
12use constant SUBUSAGE_RENEW_ONLY => 2;
13use constant SUBUSAGE_EITHER => 3;
14
41b9d8ec
TC
15sub columns {
16 return ($_[0]->SUPER::columns(),
74b21f6d 17 qw/articleId description leadTime retailPrice wholesalePrice gst options
0ec4ac8a 18 subscription_id subscription_period subscription_usage
306eb97a 19 subscription_required product_code weight length height width/ );
41b9d8ec
TC
20}
21
22sub bases {
e0ed81d7 23 return { articleId=>{ class=>'BSE::TB::Article'} };
41b9d8ec
TC
24}
25
0ec4ac8a
TC
26sub subscription_required {
27 my ($self) = @_;
28
29 my $id = $self->{subscription_required};
30 return if $id == -1;
31
32 require BSE::TB::Subscriptions;
33 return BSE::TB::Subscriptions->getByPkey($id);
34}
35
4175638b
TC
36sub subscription {
37 my ($self) = @_;
38
39 my $id = $self->{subscription_id};
40 return if $id == -1;
41
42 require BSE::TB::Subscriptions;
43 return BSE::TB::Subscriptions->getByPkey($id);
44}
45
af74f0b4
TC
46sub is_renew_sub_only {
47 my ($self) = @_;
48
49 $self->{subscription_usage} == SUBUSAGE_RENEW_ONLY;
50}
51
52sub is_start_sub_only {
53 my ($self) = @_;
54
55 $self->{subscription_usage} == SUBUSAGE_START_ONLY;
56}
57
58baa27b
TC
58sub _get_cfg_options {
59 my ($cfg) = @_;
0eb78304
TC
60
61 require BSE::CfgInfo;
62 my $avail_options = BSE::CfgInfo::product_options($cfg);
58baa27b
TC
63 my @options;
64 for my $name (keys %$avail_options) {
65 my $rawopt = $avail_options->{$name};
66 my %opt =
67 (
68 id => $name,
69 name => $rawopt->{desc},
70 default => $rawopt->{default} || '',
71 );
72 my @values;
73 for my $value (@{$rawopt->{values}}) {
74 my $label = $rawopt->{labels}{$value} || $value;
75 push @values,
76 bless
77 {
78 id => $value,
79 value => $label,
80 }, "BSE::CfgProductOptionValue";
81 }
82 $opt{values} = \@values;
83 push @options, bless \%opt, "BSE::CfgProductOption";
84 }
85
86 return @options;
87}
88
89sub _get_prod_options {
90 my ($product, $cfg, @values) = @_;
91
92 my %all_cfg_opts = map { $_->id => $_ } _get_cfg_options($cfg);
0eb78304 93 my @opt_names = split /,/, $product->{options};
58baa27b
TC
94
95 my @cfg_opts = map $all_cfg_opts{$_}, @opt_names;
96 my @db_opts = grep $_->enabled, $product->db_options;
97 my @all_options = ( @cfg_opts, @db_opts );
98
99 push @values, '' while @values < @all_options;
100
101 my $index = 0;
102 my @sem_options;
103 for my $opt (@all_options) {
104 my @opt_values = $opt->values;
105 my %opt_values = map { $_->id => $_->value } @opt_values;
84e29d01 106 my %opt_valueobjs = map { $_->id => $_ } @opt_values;
58baa27b
TC
107 my $result_opt =
108 {
109 id => $opt->key,
110 name => $opt->key,
111 desc => $opt->name,
112 value => $values[$index],
84e29d01 113 valueobj => $opt_valueobjs{$values[$index]},
9996f6d0 114 valueobjs => \%opt_valueobjs,
58baa27b
TC
115 type => $opt->type,
116 labels => \%opt_values,
117 default => $opt->default_value,
118 };
119 my $value = $values[$index];
120 if (defined $value) {
121 $result_opt->{values} = [ map $_->id, @opt_values ],
122 $result_opt->{display} = $opt_values{$values[$index]};
123 }
124 push @sem_options, $result_opt;
125 ++$index;
0eb78304
TC
126 }
127
128 return @sem_options;
129}
130
131sub option_descs {
132 my ($self, $cfg, $rvalues) = @_;
133
134 $rvalues or $rvalues = [ ];
135
136 return $self->_get_prod_options($cfg, @$rvalues);
137}
138
58baa27b
TC
139sub db_options {
140 my ($self) = @_;
141
142 require BSE::TB::ProductOptions;
66371e15
TC
143 return BSE::TB::ProductOptions->getBy2
144 (
145 [ product_id => $self->{id} ],
146 { order => "display_order" }
147 );
58baa27b
TC
148}
149
726ffaed
TC
150sub remove {
151 my ($self, $cfg) = @_;
152
153 # remove any product options
154 for my $opt ($self->db_options) {
155 $opt->remove;
156 }
157
158 # mark any order line items to "anonymize" them
159 BSE::DB->run(bseMarkProductOrderItemsAnon => $self->id);
160
161 # remove any wishlist items
162 BSE::DB->run(bseRemoveProductFromWishlists => $self->id);
163
dfd483db
TC
164 # remove any tiered prices
165 BSE::DB->run(bseRemoveProductPrices => $self->id);
166
726ffaed
TC
167 return $self->SUPER::remove($cfg);
168}
169
1d383001
TC
170sub has_sale_files {
171 my ($self) = @_;
172
173 my ($row) = BSE::DB->query(bseProductHasSaleFiles => $self->{id});
174
175 return $row->{have_sale_files};
176}
177
dfd483db
TC
178sub prices {
179 my ($self) = @_;
180
181 require BSE::TB::PriceTierPrices;
182 my @prices = BSE::TB::PriceTierPrices->getBy(product_id => $self->id);
183}
184
185=item set_prices($prices)
186
187Set tiered pricing for the product.
188
189I<$prices> is a hashref mapping tier ids to prices in cents.
190
191If a tier doesn't have a price in I<$prices> it's removed from the
192product.
193
194=cut
195
196sub set_prices {
197 my ($self, $prices) = @_;
198
199 my %current = map { $_->tier_id => $_ } $self->prices;
200 for my $tier_id (keys %$prices) {
201 my $current = delete $current{$tier_id};
202 if ($current) {
203 $current->set_retailPrice($prices->{$tier_id});
204 $current->save;
205 }
206 else {
207 BSE::TB::PriceTierPrices->make
208 (
209 tier_id => $tier_id,
210 product_id => $self->id,
211 retailPrice => $prices->{$tier_id},
212 );
213 }
214 }
215
216 # remove any spares
217 for my $price (values %current) {
218 $price->remove;
219 }
220}
221
222=item price(user => $user, date => $sql_date)
223
224=item price(user => $user)
225
226Return the retail price depending on the user and date
227and optionally the tier object (in list context).
228
229If no tier matches then the undef is returned at the tier object.
230
231=cut
232
233sub price {
234 my ($self, %opts) = @_;
235
236 my $user = delete $opts{user};
237 my $date = delete $opts{date} || BSE::Util::SQL::now_sqldate();
10dd37f9 238 my @tiers = BSE::TB::Products->pricing_tiers;
dfd483db
TC
239 my %prices = map { $_->tier_id => $_ } $self->prices;
240
241 my $price;
242 my $found_tier;
243 for my $tier (@tiers) {
244 if ($prices{$tier->id}
245 && $tier->match($user, $date)) {
246 $price = $prices{$tier->id}->retailPrice;
247 $found_tier = $tier;
248 last;
249 }
250 }
251
252 defined $price or $price = $self->retailPrice;
253
254 return wantarray ? ( $price, $found_tier ) : $price;
255}
256
257sub update_dynamic {
258 my ($self, $cfg) = @_;
259
10dd37f9 260 my @tiers = BSE::TB::Products->pricing_tiers;
dfd483db
TC
261 if (@tiers) {
262 $self->set_cached_dynamic(1);
263 return;
264 }
265
266 return $self->SUPER::update_dynamic($cfg);
267}
268
b6a28bd1 269sub tableClass {
10dd37f9 270 return "BSE::TB::Products";
b6a28bd1
TC
271}
272
58baa27b
TC
273package BSE::CfgProductOption;
274use strict;
275
276sub id { $_[0]{id} }
277
278sub key {$_[0]{id} } # same as id for config options
279
280sub type { "select" }
281
282sub name { $_[0]{name} }
283
284sub values {
285 @{$_[0]{values}}
286}
287
288sub default_value { $_[0]{default} }
289
290package BSE::CfgProductOptionValue;
291use strict;
292
293sub id { $_[0]{id} }
294
295sub value { $_[0]{value} }
296
33d04a1b
TC
297sub get_custom { undef }
298
2bd7f190
TC
299sub get_custom_all { +{} }
300
41b9d8ec 3011;