re-work coupons to allow multiple coupon types
[bse.git] / site / cgi-bin / modules / BSE / Coupon / ProductPercent.pm
1 package BSE::Coupon::ProductPercent;
2 use parent 'BSE::Coupon::Base';
3 use strict;
4
5 our $VERSION = "1.000";
6
7 sub config_fields {
8   my ($class) = @_;
9
10   require BSE::TB::Products;
11
12   return
13     {
14      discount_percent =>
15      {
16       description => "Discount",
17       required => 1,
18       width => 5,
19       htmltype => "text",
20       rules => "coupon_percent",
21       units => "%",
22       order => 1,
23      },
24      product_id =>
25      {
26       description => "Product",
27       required => 1,
28       htmltype => "select",
29       order => 2,
30       select =>
31       {
32        id => "id",
33        label => "label",
34        values =>
35        [
36         sort { lc $a->{label} cmp lc $b->{label} }
37         map
38         +{
39           id => $_->id,
40           label => $_->title,
41          }, BSE::TB::Products->all
42        ],
43       },
44      },
45      max_units =>
46      {
47       description => "Max Units",
48       required => 1,
49       width => 5,
50       htmltype => "text",
51       rules => "natural",
52       units => "units",
53       order => 3,
54      },
55     };
56 }
57
58 sub config_rules {
59   return
60     (
61     );
62 }
63
64 sub config_valid {
65   my ($self, $config, $errors) = @_;
66
67   require BSE::TB::Products;
68   my ($prod) = BSE::TB::Products->getByPkey($config->{product_id});
69   unless ($prod) {
70     $errors->{product_id} = "Unknown product id";
71   }
72   !keys %$errors;
73 }
74
75 sub class_description {
76   "Simple product specific discount";
77 }
78
79 sub _product {
80   my ($self) = @_;
81
82   unless ($self->{_product}) {
83     require BSE::TB::Products;
84     $self->{_product} = BSE::TB::Products->getByPkey($self->{config}{product_id});
85   }
86
87   $self->{_product};
88 }
89
90 sub is_active {
91   my ($self, $coupon, $cart) = @_;
92
93   for my $item ($cart->items) {
94     return 1
95       if $item->product->id == $self->{config}{product_id};
96   }
97
98   require BSE::TB::Products;
99   my ($prod) = $self->_product;
100
101   return ( 0, "This coupon only applies to ".$prod->title );
102 }
103
104 sub cart_wide {
105   0;
106 }
107
108 sub _row_discounts {
109   my ($self, $coupon, $cart) = @_;
110
111   my $max_units = $self->{config}{max_units};
112   my $product_id = $self->{config}{product_id};
113   my $discount = $self->{config}{discount_percent};
114   my $units_seen = 0;
115   my @row_discounts;
116   for my $item ($cart->items) {
117     my $discount_units = 0;
118     my $row_discount = 0;
119     if ($item->product_id == $product_id) {
120       my $prod_discount = int($item->price * $discount / 100);
121       if ($max_units) {
122         if ($units_seen < $max_units) {
123           $row_discount = $prod_discount;
124           my $units_left = $max_units - $units_seen;
125           $discount_units = $units_left > $item->units ? $item->units : $units_left;
126         }
127       }
128       else {
129         $row_discount = $prod_discount;
130         $discount_units = $item->units;
131       }
132       $units_seen += $item->units;
133     }
134     push @row_discounts, [ $row_discount, $discount_units ];
135   }
136
137   @row_discounts;
138 }
139
140 sub discount {
141   my ($self, $coupon, $cart) = @_;
142
143   my @discounts = $self->_row_discounts($coupon, $cart);
144
145   my $total_discount = 0;
146   for my $row (@discounts) {
147     $total_discount += $row->[0] * $row->[1];
148   }
149
150   return $total_discount;
151 }
152
153 sub product_discount {
154   my ($self, $coupon, $cart, $index) = @_;
155
156   my @discounts = $self->_row_discounts($coupon, $cart);
157
158   return $discounts[$index][0];
159 }
160
161 sub product_discount_units {
162   my ($self, $coupon, $cart, $index) = @_;
163
164   my @discounts = $self->_row_discounts($coupon, $cart);
165
166   return $discounts[$index][1];
167 }
168
169 sub describe {
170   my ($self) = @_;
171
172   my $desc = sprintf("%.1f%% discount on ", $self->{config}{discount_percent});
173   if ($self->{config}{max_units}) {
174     $desc .= "the first $self->{config}{max_units} units of ";
175   }
176   $desc .= $self->_product->title;
177
178   $desc;
179 }
180
181 1;