Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 8625a634

Von Sven Schöling vor fast 2 Jahren hinzugefügt

  • ID 8625a6347a5cad54b38ca3e4880a1be8bfe0e647
  • Vorgänger fd069f37
  • Nachfolger 7fe78e85

CVar + PriceRule: Manager Logik und Tests

Implementiert sind jetzt die CVar typen:
- select
- part
- customer
- vendor
- number
- date

nicht unterstützt sind:
- text
- textfield
- bool

unterstützt werden alle module:
- IC
- CT (customer und vendor, unabhängig ob der Beleg customer oder vendor
hat)
- Projects (am recorditem.project oder record.globalproject als fallback)
- Contacts (nur wenn direkt im record gesetzt)

Unterschiede anzeigen:

SL/DB/Manager/PriceRuleItem.pm
32 32
  { type => 'pricegroup', description => t8('Pricegroup'),         customer => 1, vendor => 1, data_type => 'int',  data => sub { $_[1]->pricegroup_id }, exclude_nulls => 1 },
33 33
  { type => 'partsgroup', description => t8('Partsgroup'),         customer => 1, vendor => 1, data_type => 'int',  data => sub { $_[1]->part->partsgroup_id }, exclude_nulls => 1 },
34 34
  { type => 'qty',        description => t8('Qty'),                customer => 1, vendor => 1, data_type => 'num',  data => sub { $_[1]->qty }, ops => 'num' },
35
  { type => 'cvar',       description => t8('Custom Variables'),   customer => 1, vendor => 1, data_type => 'int',  data => sub { $_[1]->part->cvar_by_name('articlegroup')->id }, exclude_nulls => 1 },
36 35
);
37 36

  
38 37
# ITEM.part.cvar_by_name(var.config.name)
......
86 85
# we only generate cvar types for cvar price_rules that are actually used to keep the query smaller
87 86
# these are cached per request
88 87
sub generate_cvar_types {
89
  my $cvar_configs = SL::DB::Manager::CustomVariableConfig->get_all(query => [
90
    id => [ \"(select distinct custom_variable_configs_id from price_rule_items where custom_variable_configs_id is not null)" ]
91
  ]);
88
  my $cvar_configs = SL::DB::Manager::CustomVariableConfig->get_all;
92 89

  
93 90
  my @types;
94 91

  
92
  # text, textfield, bool are not supported
93
  my %price_rule_type_by_cvar_type = (
94
    select    => 'text',
95
    customer  => 'int',
96
    vendor    => 'int',
97
    part      => 'int',
98
    number    => 'num',
99
    date      => 'date',
100
  );
101

  
102
  my %ops_by_cvar_type = (
103
    number    => 'num',
104
    date      => 'date',
105
  );
106

  
95 107
  for my $config (@$cvar_configs) {
108
    # cvars can be pretty complicated, but most of that luckily doesn't affect price rule filtering:
109
    #   - editable flags are copied to submodule entries - so those need to be preferred
110
    #   - cvars may be invalid - but in that case they just won't get crated so we won't find any
111
    #   - cvars may be restricted to partgroups, but again, in that case we simply won't find any
112
    #   - cvars may have different types of values () just like price_rule_items, but the ->value
113
    #     accessor already handles that, so we only need to set the price_rule type accordingly
114

  
115

  
116
    my %data_by_module = (
117
      IC => sub {
118
        raw_value(
119
          $config->processed_flags->{editable}
120
            ? $_[1]->cvar_by_name($config->name)->value
121
            : $_[1]->part->cvar_by_name($config->name)->value
122
        );
123
      },
124
      CT => sub {
125
        raw_value(
126
          $_[0]->customervendor->cvar_by_name($config->name)->value
127
        );
128
      },
129
      Projects => sub {
130
        raw_value(
131
          $_[1]->project ? $_[1]->project->cvar_by_name($config->name)->value :
132
          $_[0]->globalproject ? $_[0]->globalproject->cvar_by_name($config->name)->value : undef
133
        );
134
      },
135
      Contacts => sub {
136
        raw_value(
137
          $_[0]->contact ? $_[0]->contact->cvar_by_name($config->name)->value : undef
138
        );
139
      },
140
    );
141

  
142
    my $data_type = $price_rule_type_by_cvar_type{$config->type} or
143
      die "cvar type @{[$config->type]} is not supported in price rules";
144

  
145
    my $ops = $ops_by_cvar_type{$config->type};
96 146

  
97 147
    push @types, {
98 148
      type          => "cvar_" . $config->id,
99 149
      description   => $config->description,
100 150
      customer      => 1,
101 151
      vendor        => 1,
102
      data_type     => 'text',
103
      data          => sub {
104
        $config->processed_flags->{editable}
105
          ? $_[1]->cvar_by_name($config->name)->value
106
          : $_[1]->part->cvar_by_name($config->name)->value
107
      },
152
      data_type     => $data_type,
153
      data          => $data_by_module{$config->module},
108 154
      exclude_nulls => 1,
109 155
      cvar_config   => $config->id,
110
    } if $config->module eq 'IC' && $config->type eq 'select';
111

  
156
      ops           => $ops,
157
    };
112 158
  }
113 159

  
114 160
  @types;
115 161
}
116 162

  
163
sub raw_value {
164
  my ($value) = @_;
165
  return if !defined $value;
166
  return $value->id if (ref $value) =~ /Part|Customer|Contact|Vendor|Project/;
167
  return $value if (ref $value) =~ /DateTime/;
168
  die "reference value unsupported for binding to price_rules, got ref " . ref $value if ref $value;
169
  $value;
170
}
171

  
117 172
sub get_all_types {
118 173
  my ($class, $vc) = @_;
119 174

  
t/db/price_rule.t
12 12

  
13 13
use SL::Dev::ALL qw(:ALL);
14 14
use SL::DB::PriceRule;
15
use SL::DB::Project;
15 16
use SL::DB::CustomVariableConfig;
16 17

  
17 18
Support::TestSetup::login();
......
38 39
    description => "test",
39 40
    type        => "select",
40 41
    options     => "A##B##C##D",
42
    default_value => "D",
41 43
    flags       => "editable=0",
42 44
    searchable  => 0,
43 45
    includeable => 0,
......
83 85
    description => "test2",
84 86
    type        => "select",
85 87
    options     => "A##B##C##D",
88
    default_value => "D",
86 89
    flags       => "editable=1",
87 90
    searchable  => 0,
88 91
    includeable => 0,
......
120 123
}
121 124

  
122 125

  
126

  
127
# k, now for a more broad test:
128
#
129
# we can have these modules in cvars:
130
#  - CT
131
#  - Contact
132
#  - IC
133
#  - Project
134
#
135
# and the cvars themselves can have these types:
136
#  - select
137
#  - customer
138
#  - vendor
139
#  - part
140
#  - integer
141
#  - number
142
#  - date
143
#  - timestamp
144
#
145
#  ...with the numeric and date ones also having comparison ops
146
#
147
#
148
# to be matched against all different record/record items
149
#
150
#
151
# testing all of that is too much, so this will do some combinations:
152
#   1. a cvar config
153
#   2. a price_rule that uses both
154
#   3. record + record item that either uses that or not
155
#   4. expected behaviour
156
{
157
  sub test {
158
    my ($price_rule, $record, $record_item, $comment, $expected_match) = @_;
159

  
160
    # needed to clear cvar caches in price rule implementation
161
    $::request->{_cache} = {};
162

  
163
    my $matching_rules = SL::DB::Manager::PriceRule->get_all_matching(record => $record, record_item => $record_item);
164
    my @does_match = grep { $_->{name} eq $price_rule->name } @$matching_rules;
165

  
166
    if ($expected_match) {
167
      ok(@does_match && $price_rule->name eq $does_match[0]->name, "$comment - expected match, got @does_match");
168
    } else {
169
      ok(!@does_match, "$comment - expected no match, got @does_match");
170
    }
171
  }
172

  
173
  {
174
    reset_db();
175

  
176
    my $name = "before critical customer date";
177

  
178
    my $config = SL::DB::CustomVariableConfig->new(
179
      module => 'CT',
180
      type => 'date',
181
      name => $name,
182
      description => $name,
183
      searchable  => 0,
184
      includeable => 0,
185
      included_by_default => 0,
186
    )->save->load;
187

  
188
    my $price_rule = SL::DB::PriceRule->new(
189
      name  => $name,
190
      price => 1,
191
      type  => "customer",
192
      items => [
193
        SL::DB::PriceRuleItem->new(
194
          custom_variable_configs => $config,
195
          value_date              => DateTime->new(year => 2022, month => 12, day => 9),
196
          op                      => "lt",
197
          type                    => "cvar",
198
        ),
199
      ],
200
    )->save;
201

  
202
    my $order = create_sales_order()->save->load;
203
    my $item = $order->items_sorted->[0];
204

  
205
    test($price_rule, $order, $item, $name, 0);
206

  
207
    $order->customer->cvar_by_name($name)->value(DateTime->new(year => 2022, month => 12, day => 12));
208
    $order->customer->cvar_by_name($name)->save;
209
    test($price_rule, $order, $item, "$name -- too late", 0);
210

  
211
    $order->customer->cvar_by_name($name)->value(DateTime->new(year => 2022, month => 12, day => 5));
212
    $order->customer->cvar_by_name($name)->save;
213
    test($price_rule, $order, $item, "$name -- early", 1);
214
  }
215

  
216
  {
217
    reset_db();
218

  
219
    my $name = "contact number equals 1234";
220

  
221
    my $config = SL::DB::CustomVariableConfig->new(
222
      module => 'Contacts',
223
      type => 'number',
224
      name => $name,
225
      description => $name,
226
      searchable  => 0,
227
      includeable => 0,
228
      included_by_default => 0,
229
    )->save->load;
230

  
231
    my $price_rule = SL::DB::PriceRule->new(
232
      name  => $name,
233
      price => 1,
234
      type  => "customer",
235
      items => [
236
        SL::DB::PriceRuleItem->new(
237
          custom_variable_configs => $config,
238
          value_num               => 1234,
239
          op                      => "eq",
240
          type                    => "cvar",
241
        ),
242
      ],
243
    )->save;
244

  
245
    my $order = create_sales_order()->save->load;
246
    my $item = $order->items_sorted->[0];
247

  
248
    test($price_rule, $order, $item, "$name -- no contact", 0);
249

  
250
    $order->contact(SL::DB::Contact->new)->save;
251

  
252
    test($price_rule, $order, $item, "$name -- null", 0);
253

  
254
    $order->contact->cvar_by_name($name)->value(45);
255
    $order->contact->cvar_by_name($name)->save;
256
    test($price_rule, $order, $item, "$name -- not matching", 0);
257

  
258
    $order->contact->cvar_by_name($name)->value(1234);
259
    $order->contact->cvar_by_name($name)->save;
260
    test($price_rule, $order, $item, "$name -- matching", 1);
261
  }
262

  
263
  {
264
    reset_db();
265

  
266
    my $name = "project part matches";
267

  
268
    my $config = SL::DB::CustomVariableConfig->new(
269
      module => 'Projects',
270
      type => 'part',
271
      name => $name,
272
      description => $name,
273
      searchable  => 0,
274
      includeable => 0,
275
      included_by_default => 0,
276
    )->save->load;
277

  
278
    my $part = new_part()->save;
279

  
280
    my $price_rule = SL::DB::PriceRule->new(
281
      name  => $name,
282
      price => 1,
283
      type  => "customer",
284
      items => [
285
        SL::DB::PriceRuleItem->new(
286
          custom_variable_configs => $config,
287
          value_int               => $part->id,
288
          type                    => "cvar",
289
        ),
290
      ],
291
    )->save;
292

  
293
    my $project1 = SL::DB::Project->new(
294
      project_type   => SL::DB::Manager::ProjectType->find_by(description => 'Standard'),
295
      project_status => SL::DB::Manager::ProjectStatus->find_by(name => 'running'),
296
    )->save->load;
297

  
298
    my $order = create_sales_order()->save->load;
299
    my $item = $order->items_sorted->[0];
300

  
301
    test($price_rule, $order, $item, "$name -- no project", 0);
302

  
303
    $order->globalproject($project1)->save;
304

  
305
    test($price_rule, $order, $item, "$name -- global project, but no value", 0);
306

  
307
    $order->globalproject->cvar_by_name($name)->value($item->part);
308
    $order->globalproject->cvar_by_name($name)->save;
309
    test($price_rule, $order, $item, "$name -- global project, not matching", 0);
310

  
311
    $order->globalproject->cvar_by_name($name)->value($part);
312
    $order->globalproject->cvar_by_name($name)->save;
313
    test($price_rule, $order, $item, "$name -- global project, matching", 1);
314

  
315
    my $project2 = SL::DB::Project->new(
316
      project_type   => SL::DB::Manager::ProjectType->find_by(description => 'Standard'),
317
      project_status => SL::DB::Manager::ProjectStatus->find_by(name => 'running'),
318
    )->save->load;
319

  
320
    $item->project($project2)->save;
321

  
322
    test($price_rule, $order, $item, "$name -- item project, but no value", 0);
323

  
324
    $item->project->cvar_by_name($name)->value($item->part);
325
    $item->project->cvar_by_name($name)->save;
326
    test($price_rule, $order, $item, "$name -- item project, not matching", 0);
327

  
328
    $item->project->cvar_by_name($name)->value($part);
329
    $item->project->cvar_by_name($name)->save;
330
    test($price_rule, $order, $item, "$name -- item project, matching", 1);
331
  }
332

  
333
  {
334
    reset_db();
335

  
336
    my $name = "part customer matches";
337

  
338
    my $config = SL::DB::CustomVariableConfig->new(
339
      module => 'IC',
340
      type => 'customer',
341
      name => $name,
342
      description => $name,
343
      searchable  => 0,
344
      includeable => 0,
345
      included_by_default => 0,
346
    )->save->load;
347

  
348
    my $customer = new_customer()->save->load;
349

  
350
    my $price_rule = SL::DB::PriceRule->new(
351
      name  => $name,
352
      price => 1,
353
      type  => "vendor",
354
      items => [
355
        SL::DB::PriceRuleItem->new(
356
          custom_variable_configs => $config,
357
          value_int               => $customer->id,
358
          type                    => "cvar",
359
        ),
360
      ],
361
    )->save;
362

  
363
    my $order = create_purchase_order()->save->load;
364
    my $item = $order->items_sorted->[0];
365

  
366
    test($price_rule, $order, $item, "$name -- no value", 0);
367

  
368
    $item->part->cvar_by_name($name)->value(new_customer());
369
    $item->part->cvar_by_name($name)->save;
370
    test($price_rule, $order, $item, "$name -- not matching", 0);
371

  
372
    $item->part->cvar_by_name($name)->value($customer);
373
    $item->part->cvar_by_name($name)->save;
374
    test($price_rule, $order, $item, "$name -- matching", 1);
375
  }
376

  
377
  {
378
    reset_db();
379

  
380
    my $name = "part number with default value 15 matches 15";
381

  
382
    my $config = SL::DB::CustomVariableConfig->new(
383
      module => 'IC',
384
      type => 'number',
385
      name => $name,
386
      description => $name,
387
      default_value => 15,
388
      searchable  => 0,
389
      includeable => 0,
390
      included_by_default => 0,
391
    )->save->load;
392

  
393
    my $price_rule = SL::DB::PriceRule->new(
394
      name  => $name,
395
      price => 1,
396
      type  => "customer",
397
      items => [
398
        SL::DB::PriceRuleItem->new(
399
          custom_variable_configs => $config,
400
          value_num               => 15,
401
          op                      => "eq",
402
          type                    => "cvar",
403
        ),
404
      ],
405
    )->save;
406

  
407
    my $order = create_sales_order()->save->load;
408
    my $item = $order->items_sorted->[0];
409

  
410
    test($price_rule, $order, $item, "$name -- default value", 1);
411

  
412
    $item->part->cvar_by_name($name)->value(20);
413
    $item->part->cvar_by_name($name)->save;
414
    test($price_rule, $order, $item, "$name -- not matching", 0);
415

  
416
    $item->part->cvar_by_name($name)->value(15);
417
    $item->part->cvar_by_name($name)->save;
418
    test($price_rule, $order, $item, "$name -- matching", 1);
419
  }
420
}
421

  
422

  
123 423
done_testing();

Auch abrufbar als: Unified diff