]> wagnertech.de Git - mfinanz.git/blob - t/db/price_rule.t
date error in mapping
[mfinanz.git] / t / db / price_rule.t
1 use Test::More;
2
3 use strict;
4
5 use lib 't';
6 use utf8;
7
8 use Carp;
9 use Data::Dumper;
10 use Support::TestSetup;
11 use Test::Exception;
12
13 use SL::Controller::CustomVariableConfig;
14
15 use SL::Dev::ALL qw(:ALL);
16 use SL::DB::PriceRule;
17 use SL::DB::Project;
18 use SL::DB::CustomVariableConfig;
19
20 Support::TestSetup::login();
21
22 sub reset_db {
23   SL::DB::Manager::PriceRule->delete_all(all => 1);
24   SL::DB::Manager::CustomVariable->delete_all(all => 1);
25   SL::DB::Manager::CustomVariableConfig->delete_all(all => 1);
26   SL::DB::Manager::Order->delete_all(all => 1);
27   SL::DB::Manager::Shipto->delete_all(all => 1);
28
29   $::request->{_cache} = {};
30 }
31
32 {
33   reset_db();
34
35   # cvar price rules.
36   # a select cvar price rule for one specific value A
37   # and an order where the part has exactly that cvar set to first A and then B
38
39   my $cvar_config = SL::DB::CustomVariableConfig->new(
40     module      => 'IC',
41     name        => "test",
42     description => "test",
43     type        => "select",
44     options     => "A##B##C##D",
45     default_value => "D",
46     flags       => "editable=0",
47     searchable  => 0,
48     includeable => 0,
49     included_by_default => 0,
50   )->save->load;
51
52   my $name = "price for test A";
53
54   my $price_rule = SL::DB::PriceRule->new(
55     name  => $name,
56     price => 1,
57     type  => "customer",
58     items => [
59       SL::DB::PriceRuleItem->new(
60         custom_variable_configs => $cvar_config,
61         value_text              => "A",
62         type                    => "cvar",
63       ),
64     ],
65   )->save;
66
67   my $order = create_sales_order()->save->load;
68
69   $order->items_sorted->[0]->part->cvar_by_name('test')->value("A");
70   $order->items_sorted->[0]->part->cvar_by_name('test')->save;
71
72   ok(1 == grep({ $_->{name} eq $name } @{ SL::DB::Manager::PriceRule->get_all_matching(record => $order, record_item => $order->items_sorted->[0]) }), "editable=0 price rule matches");
73
74   $order->items_sorted->[0]->part->cvar_by_name('test')->value("B");
75   $order->items_sorted->[0]->part->cvar_by_name('test')->save;
76
77   ok(0 == grep({ $_->{name} eq $name } @{ SL::DB::Manager::PriceRule->get_all_matching(record => $order, record_item => $order->items_sorted->[0]) }), "editable=0 price rule does not match");
78 }
79
80 {
81   reset_db();
82
83   # now try the same, but with an editable cvar config
84
85   my $cvar_config = SL::DB::CustomVariableConfig->new(
86     module      => 'IC',
87     name        => "test",
88     description => "test2",
89     type        => "select",
90     options     => "A##B##C##D",
91     default_value => "D",
92     flags       => "editable=1",
93     searchable  => 0,
94     includeable => 0,
95     included_by_default => 0,
96   )->save->load;
97
98   my $name = "price for test A";
99
100   my $price_rule = SL::DB::PriceRule->new(
101     name  => $name,
102     price => 1,
103     type  => "customer",
104     items => [
105       SL::DB::PriceRuleItem->new(
106         custom_variable_configs => $cvar_config,
107         value_text              => "A",
108         type                    => "cvar",
109       ),
110     ],
111   )->save;
112
113   my $order = create_sales_order()->save->load;
114   my $item = $order->items_sorted->[0];
115
116   $item->cvar_by_name('test')->value("A");
117   $item->cvar_by_name('test')->save;
118
119   ok(1 == grep({ $_->{name} eq $name } @{ SL::DB::Manager::PriceRule->get_all_matching(record => $order, record_item => $item) }), "editable=1 price rule matches");
120
121   $item->cvar_by_name('test')->value("B");
122   $item->cvar_by_name('test')->save;
123
124   ok(0 == grep({ $_->{name} eq $name } @{ SL::DB::Manager::PriceRule->get_all_matching(record => $order, record_item => $item) }), "editable=1 price rule does not match");
125
126 }
127
128 # structural test: check whether the registered CVar types in SL::DB::Manager::PriceRuleItem have all the possible types of SL::Controller::CustomVariableConfigs
129 for (@SL::Controller::CustomVariableConfig::types) {
130   ok(exists $SL::DB::Manager::PriceRuleItem::price_rule_type_by_cvar_type{$_}, "PriceRuleItem has cvar config type $_ registered");
131 }
132
133 # k, now for a more broad test:
134 #
135 # we can have these modules in cvars:
136 #  - CT
137 #  - Contact
138 #  - IC
139 #  - Project
140 #  - ShipTo
141 #
142 # and the cvars themselves can have these types:
143 #  - select
144 #  - customer
145 #  - vendor
146 #  - part
147 #  - integer
148 #  - number
149 #  - date
150 #  - timestamp
151 #
152 #  ...with the numeric and date ones also having comparison ops
153 #
154 #
155 # to be matched against all different record/record items
156 #
157 #
158 # testing all of that is too much, so this will do some combinations:
159 #   1. a cvar config
160 #   2. a price_rule that uses both
161 #   3. record + record item that either uses that or not
162 #   4. expected behaviour
163 {
164   sub test {
165     my ($price_rule, $record, $record_item, $comment, $expected_match) = @_;
166
167     # needed to clear cvar caches in price rule implementation
168     $::request->{_cache} = {};
169
170     my $matching_rules = SL::DB::Manager::PriceRule->get_all_matching(record => $record, record_item => $record_item);
171     my @does_match = grep { $_->{name} eq $price_rule->name } @$matching_rules;
172
173     if ($expected_match) {
174       ok(@does_match && $price_rule->name eq $does_match[0]->name, "$comment - expected match, got @does_match");
175     } else {
176       ok(!@does_match, "$comment - expected no match, got @does_match");
177     }
178   }
179
180   {
181     reset_db();
182
183     my $name = "before critical customer date";
184
185     my $config = SL::DB::CustomVariableConfig->new(
186       module => 'CT',
187       type => 'date',
188       name => $name,
189       description => $name,
190       searchable  => 0,
191       includeable => 0,
192       included_by_default => 0,
193     )->save->load;
194
195     my $price_rule = SL::DB::PriceRule->new(
196       name  => $name,
197       price => 1,
198       type  => "customer",
199       items => [
200         SL::DB::PriceRuleItem->new(
201           custom_variable_configs => $config,
202           value_date              => DateTime->new(year => 2022, month => 12, day => 9),
203           op                      => "lt",
204           type                    => "cvar",
205         ),
206       ],
207     )->save;
208
209     my $order = create_sales_order()->save->load;
210     my $item = $order->items_sorted->[0];
211
212     test($price_rule, $order, $item, $name, 0);
213
214     $order->customer->cvar_by_name($name)->value(DateTime->new(year => 2022, month => 12, day => 12));
215     $order->customer->cvar_by_name($name)->save;
216     test($price_rule, $order, $item, "$name -- too late", 0);
217
218     $order->customer->cvar_by_name($name)->value(DateTime->new(year => 2022, month => 12, day => 5));
219     $order->customer->cvar_by_name($name)->save;
220     test($price_rule, $order, $item, "$name -- early", 1);
221   }
222
223   {
224     reset_db();
225
226     my $name = "contact number equals 1234";
227
228     my $config = SL::DB::CustomVariableConfig->new(
229       module => 'Contacts',
230       type => 'number',
231       name => $name,
232       description => $name,
233       searchable  => 0,
234       includeable => 0,
235       included_by_default => 0,
236     )->save->load;
237
238     my $price_rule = SL::DB::PriceRule->new(
239       name  => $name,
240       price => 1,
241       type  => "customer",
242       items => [
243         SL::DB::PriceRuleItem->new(
244           custom_variable_configs => $config,
245           value_num               => 1234,
246           op                      => "eq",
247           type                    => "cvar",
248         ),
249       ],
250     )->save;
251
252     my $order = create_sales_order()->save->load;
253     my $item = $order->items_sorted->[0];
254
255     test($price_rule, $order, $item, "$name -- no contact", 0);
256
257     $order->contact(SL::DB::Contact->new)->save;
258
259     test($price_rule, $order, $item, "$name -- null", 0);
260
261     $order->contact->cvar_by_name($name)->value(45);
262     $order->contact->cvar_by_name($name)->save;
263     test($price_rule, $order, $item, "$name -- not matching", 0);
264
265     $order->contact->cvar_by_name($name)->value(1234);
266     $order->contact->cvar_by_name($name)->save;
267     test($price_rule, $order, $item, "$name -- matching", 1);
268   }
269
270   {
271     reset_db();
272
273     my $name = "project part matches";
274
275     my $config = SL::DB::CustomVariableConfig->new(
276       module => 'Projects',
277       type => 'part',
278       name => $name,
279       description => $name,
280       searchable  => 0,
281       includeable => 0,
282       included_by_default => 0,
283     )->save->load;
284
285     my $part = new_part()->save;
286
287     my $price_rule = SL::DB::PriceRule->new(
288       name  => $name,
289       price => 1,
290       type  => "customer",
291       items => [
292         SL::DB::PriceRuleItem->new(
293           custom_variable_configs => $config,
294           value_int               => $part->id,
295           type                    => "cvar",
296         ),
297       ],
298     )->save;
299
300     my $project1 = SL::DB::Project->new(
301       project_type   => SL::DB::Manager::ProjectType->find_by(description => 'Standard'),
302       project_status => SL::DB::Manager::ProjectStatus->find_by(name => 'running'),
303     )->save->load;
304
305     my $order = create_sales_order()->save->load;
306     my $item = $order->items_sorted->[0];
307
308     test($price_rule, $order, $item, "$name -- no project", 0);
309
310     $order->globalproject($project1)->save;
311
312     test($price_rule, $order, $item, "$name -- global project, but no value", 0);
313
314     $order->globalproject->cvar_by_name($name)->value($item->part);
315     $order->globalproject->cvar_by_name($name)->save;
316     test($price_rule, $order, $item, "$name -- global project, not matching", 0);
317
318     $order->globalproject->cvar_by_name($name)->value($part);
319     $order->globalproject->cvar_by_name($name)->save;
320     test($price_rule, $order, $item, "$name -- global project, matching", 1);
321
322     my $project2 = SL::DB::Project->new(
323       project_type   => SL::DB::Manager::ProjectType->find_by(description => 'Standard'),
324       project_status => SL::DB::Manager::ProjectStatus->find_by(name => 'running'),
325     )->save->load;
326
327     $item->project($project2)->save;
328
329     test($price_rule, $order, $item, "$name -- item project, but no value", 0);
330
331     $item->project->cvar_by_name($name)->value($item->part);
332     $item->project->cvar_by_name($name)->save;
333     test($price_rule, $order, $item, "$name -- item project, not matching", 0);
334
335     $item->project->cvar_by_name($name)->value($part);
336     $item->project->cvar_by_name($name)->save;
337     test($price_rule, $order, $item, "$name -- item project, matching", 1);
338   }
339
340   {
341     reset_db();
342
343     my $name = "part customer matches";
344
345     my $config = SL::DB::CustomVariableConfig->new(
346       module => 'IC',
347       type => 'customer',
348       name => $name,
349       description => $name,
350       searchable  => 0,
351       includeable => 0,
352       included_by_default => 0,
353       flags => '',
354     )->save->load;
355
356     my $customer = new_customer()->save->load;
357
358     my $price_rule = SL::DB::PriceRule->new(
359       name  => $name,
360       price => 1,
361       type  => "vendor",
362       items => [
363         SL::DB::PriceRuleItem->new(
364           custom_variable_configs => $config,
365           value_int               => $customer->id,
366           type                    => "cvar",
367         ),
368       ],
369     )->save;
370
371     my $order = create_purchase_order()->save->load;
372     my $item = $order->items_sorted->[0];
373
374     test($price_rule, $order, $item, "$name -- no value", 0);
375
376     $item->part->cvar_by_name($name)->value(new_customer());
377     $item->part->cvar_by_name($name)->save;
378     test($price_rule, $order, $item, "$name -- not matching", 0);
379
380     $item->part->cvar_by_name($name)->value($customer);
381     $item->part->cvar_by_name($name)->save;
382     test($price_rule, $order, $item, "$name -- matching", 1);
383   }
384
385   {
386     reset_db();
387
388     my $name = "part number with default value 15 matches 15";
389
390     my $config = SL::DB::CustomVariableConfig->new(
391       module => 'IC',
392       type => 'number',
393       name => $name,
394       description => $name,
395       default_value => 15,
396       searchable  => 0,
397       includeable => 0,
398       included_by_default => 0,
399       flags => '',
400     )->save->load;
401
402     my $price_rule = SL::DB::PriceRule->new(
403       name  => $name,
404       price => 1,
405       type  => "customer",
406       items => [
407         SL::DB::PriceRuleItem->new(
408           custom_variable_configs => $config,
409           value_num               => 15,
410           op                      => "eq",
411           type                    => "cvar",
412         ),
413       ],
414     )->save;
415
416     my $order = create_sales_order()->save->load;
417     my $item = $order->items_sorted->[0];
418
419     test($price_rule, $order, $item, "$name -- default value", 1);
420
421     $item->part->cvar_by_name($name)->value(20);
422     $item->part->cvar_by_name($name)->save;
423     test($price_rule, $order, $item, "$name -- not matching", 0);
424
425     $item->part->cvar_by_name($name)->value(15);
426     $item->part->cvar_by_name($name)->save;
427     test($price_rule, $order, $item, "$name -- matching", 1);
428   }
429
430   {
431     reset_db();
432
433     my $name = "shipto cvar and price rule matching that";
434
435     my $config = SL::DB::CustomVariableConfig->new(
436       module => 'ShipTo',
437       type => 'number',
438       name => $name,
439       description => $name,
440       searchable  => 0,
441       includeable => 0,
442       included_by_default => 0,
443       flags => '',
444     )->save->load;
445
446     my $price_rule = SL::DB::PriceRule->new(
447       name  => $name,
448       price => 1,
449       type  => "customer",
450       items => [
451         SL::DB::PriceRuleItem->new(
452           custom_variable_configs => $config,
453           value_num               => 15,
454           op                      => "eq",
455           type                    => "cvar",
456         ),
457       ],
458     )->save;
459
460     my $order = create_sales_order()->save->load;
461     my $item = $order->items_sorted->[0];
462     my $shipto = SL::DB::Shipto->new;
463     $order->shipto($shipto);
464     $order->save->load;
465
466     test($price_rule, $order, $item, "$name -- default value", 0);
467
468     $order->shipto->cvar_by_name($name)->value(20);
469     $order->shipto->cvar_by_name($name)->save;
470     test($price_rule, $order, $item, "$name -- not matching", 0);
471
472     $order->shipto->cvar_by_name($name)->value(15);
473     $order->shipto->cvar_by_name($name)->save;
474     test($price_rule, $order, $item, "$name -- matching", 1);
475   }
476
477   {
478     reset_db();
479
480     my $name = "custom shipto cvar and price rule matching that";
481
482     my $config = SL::DB::CustomVariableConfig->new(
483       module => 'ShipTo',
484       type => 'number',
485       name => $name,
486       description => $name,
487       searchable  => 0,
488       includeable => 0,
489       included_by_default => 0,
490       flags => '',
491     )->save->load;
492
493     my $price_rule = SL::DB::PriceRule->new(
494       name  => $name,
495       price => 1,
496       type  => "customer",
497       items => [
498         SL::DB::PriceRuleItem->new(
499           custom_variable_configs => $config,
500           value_num               => 15,
501           op                      => "eq",
502           type                    => "cvar",
503         ),
504       ],
505     )->save;
506
507     my $order = create_sales_order()->save->load;
508     my $item = $order->items_sorted->[0];
509     my $shipto = SL::DB::Shipto->new(trans_id => $order->id, module => 'OE')->save;
510
511     ok(ref $order->custom_shipto eq 'SL::DB::Shipto', 'custom shipto is readable from order');
512
513     test($price_rule, $order, $item, "$name -- default value", 0);
514
515     $order->custom_shipto->cvar_by_name($name)->value(20);
516     $order->custom_shipto->cvar_by_name($name)->save;
517     test($price_rule, $order, $item, "$name -- not matching", 0);
518
519     $order->custom_shipto->cvar_by_name($name)->value(15);
520     $order->custom_shipto->cvar_by_name($name)->save;
521     test($price_rule, $order, $item, "$name -- matching", 1);
522   }
523
524   {
525     reset_db();
526
527     my $name = "custom shipto cvar and price rule matching that";
528
529     my $config = SL::DB::CustomVariableConfig->new(
530       module => 'ShipTo',
531       type => 'number',
532       name => $name,
533       description => $name,
534       searchable  => 0,
535       includeable => 0,
536       included_by_default => 0,
537       flags => '',
538     )->save->load;
539
540     my $price_rule = SL::DB::PriceRule->new(
541       name  => $name,
542       price => 1,
543       type  => "customer",
544       items => [
545         SL::DB::PriceRuleItem->new(
546           custom_variable_configs => $config,
547           value_num               => 15,
548           op                      => "eq",
549           type                    => "cvar",
550         ),
551       ],
552     )->save;
553
554     my $order = create_sales_order()->save->load;
555     my $item = $order->items_sorted->[0];
556     my $shipto1 = SL::DB::Shipto->new;
557     $order->shipto($shipto1);
558     my $shipto2 = SL::DB::Shipto->new(trans_id => $order->id, module => 'OE')->save;
559     $order->save->load;
560
561     test($price_rule, $order, $item, "$name -- default value", 0);
562
563     $order->custom_shipto->cvar_by_name($name)->value(20);
564     $order->custom_shipto->cvar_by_name($name)->save;
565     test($price_rule, $order, $item, "$name -- not matching custom", 0);
566
567     $order->shipto->cvar_by_name($name)->value(15);
568     $order->shipto->cvar_by_name($name)->save;
569     test($price_rule, $order, $item, "$name -- not matching custom, matching shipto", 0);
570
571     $order->custom_shipto->cvar_by_name($name)->value(15);
572     $order->custom_shipto->cvar_by_name($name)->save;
573     test($price_rule, $order, $item, "$name -- matching both", 1);
574
575     $order->shipto->cvar_by_name($name)->value(20);
576     $order->shipto->cvar_by_name($name)->save;
577     test($price_rule, $order, $item, "$name -- matching custom, not matching shipto", 1);
578   }
579
580   {
581     reset_db();
582
583     my $name = "no price rule, but cvars exist with module requirementsspecs or type text";
584
585     my $config1 = SL::DB::CustomVariableConfig->new(
586       module => 'RequirementSpecs',
587       type => 'number',
588       name => $name,
589       description => $name,
590       searchable  => 0,
591       includeable => 0,
592       included_by_default => 0,
593       flags => '',
594     )->save->load;
595
596     my $config2 = SL::DB::CustomVariableConfig->new(
597       module => 'Customer',
598       type => 'text',
599       name => $name,
600       description => $name,
601       searchable  => 0,
602       includeable => 0,
603       included_by_default => 0,
604       flags => '',
605     )->save->load;
606
607     my $order = create_sales_order()->save->load;
608     my $item = $order->items_sorted->[0];
609     $order->save->load;
610
611     test(undef, $order, $item, "$name -- nothing to match", 0);
612   }
613 }
614
615 reset_db();
616
617 done_testing();