Commit | Line | Data |
---|---|---|
ca9aa2bf TC |
1 | package BSE::Edit::Product; |
2 | use strict; | |
3 | use base 'BSE::Edit::Article'; | |
4 | use Products; | |
5 | use HTML::Entities; | |
aefcabcb | 6 | use BSE::Template; |
0ec4ac8a | 7 | use BSE::Util::Iterate; |
ab3c22ff | 8 | use DevHelp::HTML; |
2076966c | 9 | use BSE::CfgInfo 'product_options'; |
ca9aa2bf TC |
10 | |
11 | my %money_fields = | |
12 | ( | |
13 | retailPrice => "Retail price", | |
14 | wholesalePrice => "Wholesale price", | |
15 | gst => "GST", | |
16 | ); | |
17 | ||
18 | sub generator { 'Generate::Product' } | |
19 | ||
20 | sub base_template_dirs { | |
21 | return ( "products" ); | |
22 | } | |
23 | ||
24 | sub extra_templates { | |
25 | my ($self, $article) = @_; | |
26 | ||
27 | my @extras = $self->SUPER::extra_templates($article); | |
aefcabcb TC |
28 | push @extras, 'shopitem.tmpl' |
29 | if grep -f "$_/shopitem.tmpl", | |
30 | BSE::Template->template_dirs($self->{cfg}); | |
ca9aa2bf | 31 | |
d64413ee TC |
32 | my $extras = $self->{cfg}->entry('products', 'extra_templates'); |
33 | push @extras, grep /\.(tmpl|html)$/i, split /,/, $extras | |
34 | if $extras; | |
35 | ||
ca9aa2bf TC |
36 | return @extras; |
37 | } | |
38 | ||
39 | sub hash_tag { | |
40 | my ($article, $arg) = @_; | |
41 | ||
42 | my $value = $article->{$arg}; | |
43 | defined $value or $value = ''; | |
7b81711b TC |
44 | if ($value =~ /\cJ/ && $value =~ /\cM/) { |
45 | $value =~ tr/\cM//d; | |
46 | } | |
ca9aa2bf TC |
47 | |
48 | return encode_entities($value); | |
49 | } | |
50 | ||
d7538448 TC |
51 | sub iter_subs { |
52 | require BSE::TB::Subscriptions; | |
53 | BSE::TB::Subscriptions->all; | |
54 | } | |
0ec4ac8a | 55 | |
ab3c22ff TC |
56 | sub tag_hash_mbcs { |
57 | my ($object, $args) = @_; | |
58 | ||
59 | my $value = $object->{$args}; | |
60 | defined $value or $value = ''; | |
61 | if ($value =~ /\cJ/ && $value =~ /\cM/) { | |
62 | $value =~ tr/\cM//d; | |
63 | } | |
64 | escape_html($value, '<>&"'); | |
65 | } | |
66 | ||
ca9aa2bf TC |
67 | sub low_edit_tags { |
68 | my ($self, $acts, $req, $article, $articles, $msg, $errors) = @_; | |
ab3c22ff | 69 | |
2076966c TC |
70 | my $product_opts = product_options($req->cfg); |
71 | ||
ab3c22ff TC |
72 | my $cfg = $req->cfg; |
73 | my $mbcs = $cfg->entry('html', 'mbcs', 0); | |
74 | my $tag_hash = $mbcs ? \&tag_hash_mbcs : \&hash_tag; | |
0ec4ac8a | 75 | my $it = BSE::Util::Iterate->new; |
ca9aa2bf TC |
76 | return |
77 | ( | |
ab3c22ff | 78 | product => [ $tag_hash, $article ], |
ca9aa2bf TC |
79 | $self->SUPER::low_edit_tags($acts, $req, $article, $articles, $msg, |
80 | $errors), | |
2076966c | 81 | alloptions => join(",", sort keys %$product_opts), |
d7538448 TC |
82 | $it->make_iterator |
83 | ([ \&iter_subs, $req ], 'subscription', 'subscriptions'), | |
ca9aa2bf TC |
84 | ); |
85 | } | |
86 | ||
87 | sub edit_template { | |
88 | my ($self, $article, $cgi) = @_; | |
89 | ||
90 | my $base = 'product'; | |
91 | my $t = $cgi->param('_t'); | |
92 | if ($t && $t =~ /^\w+$/) { | |
93 | $base = $t; | |
94 | } | |
95 | return $self->{cfg}->entry('admin templates', $base, | |
96 | "admin/edit_$base"); | |
97 | } | |
98 | ||
99 | sub add_template { | |
100 | my ($self, $article, $cgi) = @_; | |
101 | ||
102 | return $self->{cfg}->entry('admin templates', 'add_product', | |
918735d1 | 103 | 'admin/edit_product'); |
ca9aa2bf TC |
104 | } |
105 | ||
106 | sub validate_parent { | |
107 | my ($self, $data, $articles, $parent, $rmsg) = @_; | |
108 | ||
109 | my $shopid = $self->{cfg}->entryErr('articles', 'shop'); | |
110 | unless ($parent && | |
111 | $parent->{generator} eq 'Generate::Catalog') { | |
112 | $$rmsg = "Products must be in a catalog (not $parent->{generator})"; | |
113 | return; | |
114 | } | |
115 | ||
116 | return $self->SUPER::validate_parent($data, $articles, $parent, $rmsg); | |
117 | } | |
118 | ||
119 | sub _validate_common { | |
120 | my ($self, $data, $articles, $errors) = @_; | |
121 | ||
122 | for my $col (keys %money_fields) { | |
123 | my $value = $data->{$col}; | |
0ec4ac8a | 124 | defined $value or next; |
ca9aa2bf TC |
125 | unless ($value =~ /^\d+(\.\d{1,2})?\s*/) { |
126 | $errors->{$col} = "$money_fields{$col} invalid"; | |
127 | } | |
128 | } | |
2076966c | 129 | |
0ec4ac8a | 130 | if (defined $data->{options}) { |
2076966c TC |
131 | my $avail_options = product_options($self->{cfg}); |
132 | ||
133 | my @bad_opts = grep !$avail_options->{$_}, | |
0ec4ac8a TC |
134 | split /,/, $data->{options}; |
135 | if (@bad_opts) { | |
136 | $errors->{options} = "Bad product options '". join(",", @bad_opts)."' entered"; | |
137 | } | |
138 | } | |
ca9aa2bf | 139 | |
0ec4ac8a TC |
140 | my @subs; |
141 | for my $sub_field (qw(subscription_id subscription_required)) { | |
142 | my $value = $data->{$sub_field}; | |
143 | defined $value or next; | |
144 | if ($value ne '-1') { | |
d7538448 TC |
145 | require BSE::TB::Subscriptions; |
146 | @subs = BSE::TB::Subscriptions->all unless @subs; | |
147 | unless (grep $_->{subscription_id} == $value, @subs) { | |
0ec4ac8a | 148 | $errors->{$sub_field} = "Invalid $sub_field value"; |
d7538448 | 149 | } |
0ec4ac8a TC |
150 | } |
151 | } | |
152 | if (defined $data->{subscription_period}) { | |
ab2cd916 TC |
153 | my $sub = $data->{subscription_id}; |
154 | if ($data->{subscription_period} !~ /^\d+$/) { | |
0ec4ac8a TC |
155 | $errors->{subscription_period} = "Invalid subscription period, it must be the number of months to subscribe"; |
156 | } | |
ab2cd916 TC |
157 | elsif ($sub != -1 && $data->{subscription_period} < 1) { |
158 | $errors->{subscription_period} = "Subscription period must be 1 or more when a subscription is selected"; | |
159 | } | |
0ec4ac8a TC |
160 | } |
161 | if (defined $data->{subscription_usage}) { | |
162 | unless ($data->{subscription_usage} =~ /^[123]$/) { | |
163 | $errors->{subscription_usage} = "Invalid subscription usage"; | |
164 | } | |
ca9aa2bf TC |
165 | } |
166 | ||
167 | return !keys %$errors; | |
168 | } | |
169 | ||
170 | sub validate { | |
918735d1 | 171 | my ($self, $data, $articles, $errors) = @_; |
ca9aa2bf | 172 | |
918735d1 | 173 | my $ok = $self->SUPER::validate($data, $articles, $errors); |
ca9aa2bf TC |
174 | $self->_validate_common($data, $articles, $errors); |
175 | ||
7fa9d326 | 176 | for my $field (qw(title description body)) { |
ca9aa2bf TC |
177 | unless ($data->{$field} =~ /\S/) { |
178 | $errors->{$field} = "No $field entered"; | |
179 | } | |
180 | } | |
181 | ||
182 | return $ok && !keys %$errors; | |
183 | } | |
184 | ||
185 | sub validate_old { | |
918735d1 | 186 | my ($self, $article, $data, $articles, $errors) = @_; |
ca9aa2bf | 187 | |
918735d1 | 188 | $self->SUPER::validate($data, $articles, $errors) |
ca9aa2bf TC |
189 | or return; |
190 | ||
191 | return !keys %$errors; | |
192 | } | |
193 | ||
194 | sub possible_parents { | |
195 | my ($self, $article, $articles) = @_; | |
196 | ||
197 | my %labels; | |
198 | my @values; | |
199 | ||
200 | my $shopid = $self->{cfg}->entryErr('articles', 'shop'); | |
201 | # the parents of a catalog can be other catalogs or the shop | |
202 | my $shop = $articles->getByPkey($shopid); | |
203 | my @work = [ $shopid, $shop->{title} ]; | |
204 | while (@work) { | |
205 | my ($id, $title) = @{pop @work}; | |
206 | push(@values, $id); | |
207 | $labels{$id} = $title; | |
208 | push @work, map [ $_->{id}, $title.' / '.$_->{title} ], | |
209 | sort { $b->{displayOrder} <=> $a->{displayOrder} } | |
210 | grep $_->{generator} eq 'Generate::Catalog', | |
211 | $articles->getBy(parentid=>$id); | |
212 | } | |
a5e3fc4b TC |
213 | unless ($shop->{generator} eq 'Generate::Catalog') { |
214 | shift @values; | |
215 | delete $labels{$shopid}; | |
216 | } | |
ca9aa2bf TC |
217 | return (\@values, \%labels); |
218 | } | |
219 | ||
220 | sub table_object { | |
221 | my ($self, $articles) = @_; | |
222 | ||
223 | 'Products'; | |
224 | } | |
225 | ||
226 | sub get_article { | |
227 | my ($self, $articles, $article) = @_; | |
228 | ||
229 | return Products->getByPkey($article->{id}); | |
230 | } | |
231 | ||
95989433 TC |
232 | sub default_link_path { |
233 | my ($self, $article) = @_; | |
234 | ||
235 | $self->{cfg}->entry('uri', 'shop', '/shop'); | |
236 | } | |
237 | ||
ca9aa2bf TC |
238 | sub make_link { |
239 | my ($self, $article) = @_; | |
240 | ||
57d988af TC |
241 | # Modified by adrian |
242 | my $urlbase = ''; | |
243 | if ($self->{cfg}->entry('shop', 'secureurl_articles', 1)) { | |
244 | $urlbase = $self->{cfg}->entryVar('site', 'secureurl'); | |
245 | } | |
246 | # end adrian | |
efcc5a30 TC |
247 | |
248 | if ($article->is_dynamic) { | |
b873a8fa | 249 | return "$urlbase/cgi-bin/page.pl?page=$article->{id}&title=".escape_uri($article->{title}); |
efcc5a30 TC |
250 | } |
251 | ||
252 | my $shop_uri = $self->link_path($article); | |
ca9aa2bf TC |
253 | return $urlbase.$shop_uri."/shop$article->{id}.html"; |
254 | } | |
255 | ||
256 | sub _fill_product_data { | |
257 | my ($self, $req, $data, $src) = @_; | |
258 | ||
259 | for my $money_col (qw(retailPrice wholesalePrice gst)) { | |
260 | if (exists $src->{$money_col}) { | |
261 | if ($src->{$money_col} =~ /^\d+(\.\d\d)?\s*/) { | |
262 | $data->{$money_col} = 100 * $src->{$money_col}; | |
263 | } | |
264 | else { | |
265 | $data->{$money_col} = 0; | |
266 | } | |
267 | } | |
268 | } | |
269 | if (exists $src->{leadTime}) { | |
270 | $src->{leadTime} =~ /^\d+\s*$/ | |
271 | or $src->{leadTime} = 0; | |
272 | $data->{leadTime} = $src->{leadTime}; | |
273 | } | |
74b21f6d | 274 | if (exists $src->{description} && length $src->{description}) { |
4010d92e | 275 | if ($data->{id}) { |
74b21f6d TC |
276 | if ($req->user_can('edit_field_edit_description', $data)) { |
277 | $data->{description} = $src->{description}; | |
278 | } | |
279 | } | |
280 | } | |
281 | if (exists $src->{product_code} && length $src->{product_code}) { | |
282 | if ($data->{id}) { | |
283 | if ($req->user_can('edit_field_edit_product_code', $data)) { | |
284 | $data->{product_code} = $src->{product_code}; | |
4010d92e TC |
285 | } |
286 | } | |
287 | } | |
0ec4ac8a TC |
288 | for my $field (qw(options subscription_id subscription_period |
289 | subscription_usage subscription_required)) { | |
290 | if (exists $src->{$field}) { | |
291 | $data->{$field} = $src->{$field}; | |
292 | } | |
293 | elsif ($data == $src) { | |
294 | # use the default | |
295 | $data->{$field} = $self->default_value($req, $data, $field); | |
296 | } | |
918735d1 | 297 | } |
ca9aa2bf TC |
298 | } |
299 | ||
300 | sub fill_new_data { | |
301 | my ($self, $req, $data, $articles) = @_; | |
302 | ||
303 | $self->_fill_product_data($req, $data, $data); | |
304 | ||
305 | return $self->SUPER::fill_new_data($req, $data, $articles); | |
306 | } | |
307 | ||
308 | sub fill_old_data { | |
309 | my ($self, $req, $article, $src) = @_; | |
310 | ||
311 | $self->_fill_product_data($req, $article, $src); | |
312 | ||
313 | return $self->SUPER::fill_old_data($req, $article, $src); | |
314 | } | |
315 | ||
caa7299c TC |
316 | sub default_template { |
317 | my ($self, $article, $cfg, $templates) = @_; | |
318 | ||
d64413ee | 319 | my $template = $cfg->entry('products', 'template'); |
caa7299c TC |
320 | return $template |
321 | if $template && grep $_ eq $template, @$templates; | |
322 | ||
323 | return $self->SUPER::default_template($article, $cfg, $templates); | |
324 | } | |
325 | ||
6473c56f TC |
326 | sub can_remove { |
327 | my ($self, $req, $article, $articles, $rmsg) = @_; | |
328 | ||
4175638b TC |
329 | require BSE::TB::OrderItems; |
330 | my @items = BSE::TB::OrderItems->getBy(productId=>$article->{id}); | |
6473c56f TC |
331 | if (@items) { |
332 | $$rmsg = "There are orders for this product. It cannot be deleted."; | |
333 | return; | |
334 | } | |
335 | ||
336 | return $self->SUPER::can_remove($req, $article, $articles, $rmsg); | |
337 | } | |
338 | ||
918735d1 TC |
339 | sub flag_sections { |
340 | my ($self) = @_; | |
341 | ||
342 | return ( 'product flags', $self->SUPER::flag_sections ); | |
343 | } | |
344 | ||
0ec4ac8a TC |
345 | my %defaults = |
346 | ( | |
347 | options => '', | |
348 | subscription_id => -1, | |
349 | subscription_required => -1, | |
350 | subscription_period => 1, | |
351 | subscription_usage => 3, | |
352 | retailPrice => 0, | |
353 | ); | |
354 | ||
355 | sub default_value { | |
356 | my ($self, $req, $article, $col) = @_; | |
357 | ||
358 | my $value = $self->SUPER::default_value($req, $article, $col); | |
359 | defined $value and return $value; | |
360 | ||
361 | exists $defaults{$col} and return $defaults{$col}; | |
362 | ||
363 | return; | |
364 | } | |
365 | ||
deae2a52 TC |
366 | sub type_default_value { |
367 | my ($self, $req, $col) = @_; | |
368 | ||
369 | my $value = $req->cfg->entry('product defaults', $col); | |
370 | defined $value and return $value; | |
371 | ||
372 | return $self->SUPER::type_default_value($req, $col); | |
373 | } | |
374 | ||
ca9aa2bf | 375 | 1; |