Revision 9589ecd7
Von Sven Schöling vor etwa 10 Jahren hinzugefügt
SL/Controller/PriceRule.pm | ||
---|---|---|
1 |
package SL::Controller::PriceRule; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(SL::Controller::Base); |
|
6 |
|
|
7 |
use SL::Controller::Helper::GetModels; |
|
8 |
use SL::Controller::Helper::ParseFilter; |
|
9 |
use SL::Controller::Helper::ReportGenerator; |
|
10 |
use SL::DB::PriceRule; |
|
11 |
use SL::DB::PriceRuleItem; |
|
12 |
use SL::DB::Pricegroup; |
|
13 |
use SL::DB::PartsGroup; |
|
14 |
use SL::DB::Business; |
|
15 |
use SL::Helper::Flash; |
|
16 |
use SL::ClientJS; |
|
17 |
use SL::Locale::String; |
|
18 |
|
|
19 |
use Rose::Object::MakeMethods::Generic |
|
20 |
( |
|
21 |
'scalar --get_set_init' => [ qw(models price_rule vc js pricegroups partsgroups businesses) ], |
|
22 |
); |
|
23 |
|
|
24 |
# __PACKAGE__->run_before('check_auth'); |
|
25 |
__PACKAGE__->run_before('add_javascripts'); |
|
26 |
|
|
27 |
# |
|
28 |
# actions |
|
29 |
# |
|
30 |
|
|
31 |
sub action_list { |
|
32 |
my ($self) = @_; |
|
33 |
|
|
34 |
$self->make_filter_summary; |
|
35 |
|
|
36 |
my $price_rules = $self->models->get; |
|
37 |
|
|
38 |
$self->prepare_report; |
|
39 |
|
|
40 |
$self->report_generator_list_objects(report => $self->{report}, objects => $price_rules); |
|
41 |
} |
|
42 |
|
|
43 |
sub action_new { |
|
44 |
my ($self) = @_; |
|
45 |
|
|
46 |
$self->price_rule(SL::DB::PriceRule->new); |
|
47 |
$self->price_rule->assign_attributes(%{ $::form->{price_rule} || {} }); |
|
48 |
$self->display_form; |
|
49 |
} |
|
50 |
|
|
51 |
sub action_edit { |
|
52 |
my ($self) = @_; |
|
53 |
|
|
54 |
$self->display_form; |
|
55 |
} |
|
56 |
|
|
57 |
sub action_create { |
|
58 |
my ($self) = @_; |
|
59 |
|
|
60 |
$self->price_rule($self->price_rule->clone_and_reset_deep); |
|
61 |
$self->create_or_update; |
|
62 |
} |
|
63 |
|
|
64 |
sub action_update { |
|
65 |
my ($self) = @_; |
|
66 |
$self->create_or_update; |
|
67 |
} |
|
68 |
|
|
69 |
#TODO |
|
70 |
sub action_destroy { |
|
71 |
my ($self) = @_; |
|
72 |
|
|
73 |
$self->price_rule->obsolete(1); |
|
74 |
$self->price_rule->save; |
|
75 |
flash_later('info', $::locale->text('The price rule has been obsoleted.')); |
|
76 |
|
|
77 |
$self->redirect_to(action => 'list'); |
|
78 |
} |
|
79 |
|
|
80 |
sub action_add_item_row { |
|
81 |
my ($self, %params) = @_; |
|
82 |
|
|
83 |
my $item = SL::DB::PriceRuleItem->new(type => $::form->{type}); |
|
84 |
|
|
85 |
my $html = $self->render('price_rule/item', { output => 0 }, item => $item); |
|
86 |
|
|
87 |
$self |
|
88 |
->js |
|
89 |
->before('#price_rule_new_items', $html) |
|
90 |
->reinit_widgets |
|
91 |
->render($self); |
|
92 |
} |
|
93 |
|
|
94 |
# |
|
95 |
# filters |
|
96 |
# |
|
97 |
|
|
98 |
sub check_auth { |
|
99 |
$::auth->assert('price_rule_edit'); |
|
100 |
} |
|
101 |
|
|
102 |
# |
|
103 |
# helpers |
|
104 |
# |
|
105 |
|
|
106 |
sub display_form { |
|
107 |
my ($self, %params) = @_; |
|
108 |
my $is_new = !$self->price_rule->id; |
|
109 |
my $title = $is_new ? t8('Create a new price rule') : t8('Edit price rule'); |
|
110 |
$self->render('price_rule/form', |
|
111 |
title => $title, |
|
112 |
%params |
|
113 |
); |
|
114 |
} |
|
115 |
|
|
116 |
sub create_or_update { |
|
117 |
my $self = shift; |
|
118 |
my $is_new = !$self->price_rule->id; |
|
119 |
my $params = delete($::form->{price_rule}) || { }; |
|
120 |
|
|
121 |
delete $params->{id}; |
|
122 |
$self->price_rule->assign_attributes(%{ $params }); |
|
123 |
|
|
124 |
my @errors = $self->price_rule->validate; |
|
125 |
|
|
126 |
if (@errors) { |
|
127 |
flash('error', @errors); |
|
128 |
$self->display_form(callback => $::form->{callback}); |
|
129 |
return; |
|
130 |
} |
|
131 |
|
|
132 |
$self->price_rule->save; |
|
133 |
|
|
134 |
flash_later('info', $is_new ? $::locale->text('The price rule has been created.') : $::locale->text('The price rule has been saved.')); |
|
135 |
|
|
136 |
$self->redirect_to($::form->{callback} || (action => 'list', 'filter.type' => $self->price_rule->type)); |
|
137 |
} |
|
138 |
|
|
139 |
sub prepare_report { |
|
140 |
my ($self) = @_; |
|
141 |
|
|
142 |
my $callback = $self->models->get_callback; |
|
143 |
|
|
144 |
my $report = SL::ReportGenerator->new(\%::myconfig, $::form); |
|
145 |
$self->{report} = $report; |
|
146 |
|
|
147 |
my @columns = qw(name type priority price discount); |
|
148 |
my @sortable = qw(name type priority price discount); |
|
149 |
|
|
150 |
my %column_defs = ( |
|
151 |
name => { obj_link => sub { $self->url_for(action => 'edit', 'price_rule.id' => $_[0]->id, callback => $callback) } }, |
|
152 |
priority => { sub => sub { $_[0]->priority } }, |
|
153 |
price => { sub => sub { $_[0]->price_as_number } }, |
|
154 |
discount => { sub => sub { $_[0]->discount_as_number } }, |
|
155 |
obsolete => { sub => sub { $_[0]->obsolete_as_bool_yn } }, |
|
156 |
); |
|
157 |
|
|
158 |
map { $column_defs{$_}->{text} ||= $::locale->text( $self->models->get_sort_spec->{$_}->{title} ) } keys %column_defs; |
|
159 |
|
|
160 |
if ( $report->{options}{output_format} =~ /^(pdf|csv)$/i ) { |
|
161 |
$self->models->disable_plugin('paginated'); |
|
162 |
} |
|
163 |
$report->set_options( |
|
164 |
std_column_visibility => 1, |
|
165 |
controller_class => 'PriceRule', |
|
166 |
output_format => 'HTML', |
|
167 |
title => $::locale->text('Price Rules'), |
|
168 |
allow_pdf_export => 1, |
|
169 |
allow_csv_export => 1, |
|
170 |
); |
|
171 |
$report->set_columns(%column_defs); |
|
172 |
$report->set_column_order(@columns); |
|
173 |
$report->set_export_options(qw(list filter)); |
|
174 |
$report->set_options_from_form; |
|
175 |
$self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable); |
|
176 |
$report->set_options( |
|
177 |
raw_bottom_info_text => $self->render('price_rule/report_bottom', { output => 0 }), |
|
178 |
raw_top_info_text => $self->render('price_rule/report_top', { output => 0 }), |
|
179 |
); |
|
180 |
} |
|
181 |
|
|
182 |
sub make_filter_summary { |
|
183 |
my ($self) = @_; |
|
184 |
|
|
185 |
my $filter = $::form->{filter} || {}; |
|
186 |
my @filter_strings; |
|
187 |
|
|
188 |
my @filters = ( |
|
189 |
[ $filter->{"name:substr::ilike"}, t8('Name') ], |
|
190 |
[ $filter->{"price:number"}, t8('Price') ], |
|
191 |
[ $filter->{"discount:number"}, t8('Discount') ], |
|
192 |
); |
|
193 |
|
|
194 |
for (@filters) { |
|
195 |
push @filter_strings, "$_->[1]: $_->[0]" if $_->[0]; |
|
196 |
} |
|
197 |
|
|
198 |
$self->{filter_summary} = join ', ', @filter_strings; |
|
199 |
} |
|
200 |
|
|
201 |
sub all_price_rule_item_types { |
|
202 |
SL::DB::Manager::PriceRuleItem->get_all_types($_[0]->price_rule->type); |
|
203 |
} |
|
204 |
|
|
205 |
sub add_javascripts { |
|
206 |
$::request->{layout}->add_javascripts(qw(kivi.PriceRule.js autocomplete_customer.js autocomplete_vendor.js)); |
|
207 |
} |
|
208 |
|
|
209 |
sub init_price_rule { |
|
210 |
my ($self) = @_; |
|
211 |
|
|
212 |
my $price_rule = $::form->{price_rule}{id} ? SL::DB::PriceRule->new(id => $::form->{price_rule}{id})->load : SL::DB::PriceRule->new; |
|
213 |
|
|
214 |
my $items = delete $::form->{price_rule}{items}; |
|
215 |
|
|
216 |
$price_rule->assign_attributes(%{ $::form->{price_rule} || {} }); |
|
217 |
|
|
218 |
my %old_items = map { $_->id => $_ } $price_rule->items; |
|
219 |
|
|
220 |
my @items; |
|
221 |
for my $raw_item (@$items) { |
|
222 |
my $item = $raw_item->{id} ? $old_items{ $raw_item->{id} } || SL::DB::PriceRuleItem->new(id => $raw_item->{id})->load : SL::DB::PriceRuleItem->new; |
|
223 |
$item->assign_attributes(%$raw_item); |
|
224 |
push @items, $item; |
|
225 |
} |
|
226 |
|
|
227 |
$price_rule->items(@items) if @items; |
|
228 |
|
|
229 |
$self->price_rule($price_rule); |
|
230 |
} |
|
231 |
|
|
232 |
sub init_vc { |
|
233 |
$::form->{filter}{type}; |
|
234 |
} |
|
235 |
|
|
236 |
sub init_js { |
|
237 |
SL::ClientJS->new; |
|
238 |
} |
|
239 |
|
|
240 |
sub init_businesses { |
|
241 |
SL::DB::Manager::Business->get_all; |
|
242 |
} |
|
243 |
|
|
244 |
sub init_pricegroups { |
|
245 |
SL::DB::Manager::Pricegroup->get_all; |
|
246 |
} |
|
247 |
|
|
248 |
sub init_partsgroups { |
|
249 |
SL::DB::Manager::PartsGroup->get_all; |
|
250 |
} |
|
251 |
|
|
252 |
sub init_models { |
|
253 |
my ($self) = @_; |
|
254 |
|
|
255 |
SL::Controller::Helper::GetModels->new( |
|
256 |
controller => $self, |
|
257 |
sorted => { |
|
258 |
name => t8('Name'), |
|
259 |
type => t8('Type'), |
|
260 |
priority => t8('Priority'), |
|
261 |
price => t8('Price'), |
|
262 |
discount => t8('Discount'), |
|
263 |
obsolete => t8('Obsolete'), |
|
264 |
}, |
|
265 |
); |
|
266 |
} |
|
267 |
|
|
268 |
1; |
SL/DB/Helper/ALL.pm | ||
---|---|---|
68 | 68 |
use SL::DB::PriceFactor; |
69 | 69 |
use SL::DB::Pricegroup; |
70 | 70 |
use SL::DB::Price; |
71 |
use SL::DB::PriceRule; |
|
72 |
use SL::DB::PriceRuleItem; |
|
71 | 73 |
use SL::DB::Printer; |
72 | 74 |
use SL::DB::Project; |
73 | 75 |
use SL::DB::ProjectParticipant; |
SL/DB/Helper/Mappings.pm | ||
---|---|---|
148 | 148 |
periodic_invoices_configs => 'periodic_invoices_config', |
149 | 149 |
prices => 'price', |
150 | 150 |
price_factors => 'price_factor', |
151 |
price_rules => 'price_rule', |
|
152 |
price_rule_items => 'price_rule_item', |
|
151 | 153 |
pricegroup => 'pricegroup', |
152 | 154 |
printers => 'printer', |
153 | 155 |
project => 'project', |
SL/DB/Manager/PriceRule.pm | ||
---|---|---|
1 |
# This file as been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::Manager::PriceRule; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use parent qw(SL::DB::Helper::Manager); |
|
9 |
|
|
10 |
use SL::DB::Helper::Filtered; |
|
11 |
use SL::DB::Helper::Paginated; |
|
12 |
use SL::DB::Helper::Sorted; |
|
13 |
use SL::DBUtils; |
|
14 |
|
|
15 |
use SL::Locale::String qw(t8); |
|
16 |
|
|
17 |
sub object_class { 'SL::DB::PriceRule' } |
|
18 |
|
|
19 |
__PACKAGE__->make_manager_methods; |
|
20 |
|
|
21 |
sub get_matching_filter { |
|
22 |
my ($class, %params) = @_; |
|
23 |
|
|
24 |
die 'need record' unless $params{record}; |
|
25 |
die 'need record_item' unless $params{record_item}; |
|
26 |
|
|
27 |
my $type = $params{record}->is_sales ? 'customer' : 'vendor'; |
|
28 |
|
|
29 |
# plan: 1. search all rule_items that do NOT match this record/record item combo |
|
30 |
my ($sub_where, @value_subs) = SL::DB::Manager::PriceRuleItem->not_matching_sql_and_values(type => $type); |
|
31 |
my @values = map { $_->($params{record}, $params{record_item}) } @value_subs; |
|
32 |
|
|
33 |
# now union all NOT matching, invert ids, load these |
|
34 |
my $matching_rule_ids = <<SQL; |
|
35 |
SELECT id FROM price_rules |
|
36 |
WHERE id NOT IN ( |
|
37 |
SELECT price_rules_id FROM price_rule_items WHERE $sub_where |
|
38 |
) |
|
39 |
AND type = ? AND NOT obsolete |
|
40 |
SQL |
|
41 |
|
|
42 |
push @values, $type; |
|
43 |
|
|
44 |
return $matching_rule_ids, @values; |
|
45 |
} |
|
46 |
|
|
47 |
sub get_all_matching { |
|
48 |
my ($self, %params) = @_; |
|
49 |
|
|
50 |
my ($query, @values) = $self->get_matching_filter(%params); |
|
51 |
my @ids = selectall_ids($::form, $::form->get_standard_dbh, $query, 0, @values); |
|
52 |
|
|
53 |
$self->get_all(query => [ id => \@ids ]); |
|
54 |
} |
|
55 |
|
|
56 |
sub _sort_spec { |
|
57 |
return ( columns => { SIMPLE => 'ALL', }, |
|
58 |
default => [ 'name', 1 ], |
|
59 |
nulls => { price => 'LAST', discount => 'LAST' } |
|
60 |
); |
|
61 |
} |
|
62 |
|
|
63 |
1; |
SL/DB/Manager/PriceRuleItem.pm | ||
---|---|---|
1 |
# This file has been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::Manager::PriceRuleItem; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use SL::DB::Helper::Manager; |
|
9 |
use base qw(SL::DB::Helper::Manager); |
|
10 |
|
|
11 |
sub object_class { 'SL::DB::PriceRuleItem' } |
|
12 |
|
|
13 |
__PACKAGE__->make_manager_methods; |
|
14 |
|
|
15 |
use SL::Locale::String qw(t8); |
|
16 |
|
|
17 |
my @types = qw( |
|
18 |
customer vendor business partsgroup qty reqdate pricegroup |
|
19 |
); |
|
20 |
|
|
21 |
my %ops = ( |
|
22 |
'num' => { eq => '=', lt => '<', gt => '>' }, |
|
23 |
'date' => { eq => '=', lt => '<', gt => '>' }, |
|
24 |
); |
|
25 |
|
|
26 |
my %types = ( |
|
27 |
'customer' => { description => t8('Customer'), customer => 1, vendor => 0, data_type => 'int', data => sub { $_[0]->customer->id }, }, |
|
28 |
'vendor' => { description => t8('Vendor'), customer => 0, vendor => 1, data_type => 'int', data => sub { $_[0]->vendor->id }, }, |
|
29 |
'business' => { description => t8('Type of Business'), customer => 1, vendor => 1, data_type => 'int', data => sub { $_[0]->customervendor->business_id }, }, |
|
30 |
'reqdate' => { description => t8('Reqdate'), customer => 1, vendor => 1, data_type => 'date', data => sub { $_[0]->reqdate }, ops => 'date' }, |
|
31 |
'pricegroup' => { description => t8('Pricegroup'), customer => 1, vendor => 1, data_type => 'int', data => sub { $_[1]->pricegroup_id }, }, |
|
32 |
'partsgroup' => { description => t8('Group'), customer => 1, vendor => 1, data_type => 'int', data => sub { $_[1]->part->partsgroup_id }, }, |
|
33 |
'qty' => { description => t8('Qty'), customer => 1, vendor => 1, data_type => 'num', data => sub { $_[1]->qty }, ops => 'num' }, |
|
34 |
); |
|
35 |
|
|
36 |
sub not_matching_sql_and_values { |
|
37 |
my ($class, %params) = @_; |
|
38 |
|
|
39 |
die 'must be called with a customer/vendor type' unless $params{type}; |
|
40 |
|
|
41 |
my (@tokens, @values); |
|
42 |
|
|
43 |
for my $type (@types) { |
|
44 |
my $def = $types{$type}; |
|
45 |
next unless $def->{$params{type}}; |
|
46 |
|
|
47 |
if ($def->{ops}) { |
|
48 |
my $ops = $ops{$def->{ops}}; |
|
49 |
|
|
50 |
my @sub_tokens; |
|
51 |
for (keys %$ops) { |
|
52 |
push @sub_tokens, "op = '$_' AND NOT value_$def->{data_type} $ops->{$_} ?"; |
|
53 |
push @values, $def->{data}; |
|
54 |
} |
|
55 |
|
|
56 |
push @tokens, "type = '$type' AND " . join ' OR ', map "($_)", @sub_tokens; |
|
57 |
} else { |
|
58 |
push @tokens, "type = '$type' AND NOT value_$def->{data_type} = ?"; |
|
59 |
push @values, $def->{data}; |
|
60 |
} |
|
61 |
} |
|
62 |
|
|
63 |
return join(' OR ', map "($_)", @tokens), @values; |
|
64 |
} |
|
65 |
|
|
66 |
sub get_all_types { |
|
67 |
my ($class, $vc) = @_; |
|
68 |
|
|
69 |
[ map { [ $_, $types{$_}{description} ] } grep { $types{$_}{$vc} } map { $_ } @types ]; |
|
70 |
} |
|
71 |
|
|
72 |
1; |
SL/DB/MetaSetup/PriceRule.pm | ||
---|---|---|
1 |
# This file has been auto-generated. Do not modify it; it will be overwritten |
|
2 |
# by rose_auto_create_model.pl automatically. |
|
3 |
package SL::DB::PriceRule; |
|
4 |
|
|
5 |
use strict; |
|
6 |
|
|
7 |
use base qw(SL::DB::Object); |
|
8 |
|
|
9 |
__PACKAGE__->meta->table('price_rules'); |
|
10 |
|
|
11 |
__PACKAGE__->meta->columns( |
|
12 |
discount => { type => 'numeric', precision => 15, scale => 5 }, |
|
13 |
id => { type => 'serial', not_null => 1 }, |
|
14 |
itime => { type => 'timestamp' }, |
|
15 |
mtime => { type => 'timestamp' }, |
|
16 |
name => { type => 'text' }, |
|
17 |
obsolete => { type => 'boolean', default => 'false', not_null => 1 }, |
|
18 |
price => { type => 'numeric', precision => 15, scale => 5 }, |
|
19 |
priority => { type => 'integer', default => 3, not_null => 1 }, |
|
20 |
type => { type => 'text' }, |
|
21 |
); |
|
22 |
|
|
23 |
__PACKAGE__->meta->primary_key_columns([ 'id' ]); |
|
24 |
|
|
25 |
1; |
|
26 |
; |
SL/DB/MetaSetup/PriceRuleItem.pm | ||
---|---|---|
1 |
# This file has been auto-generated. Do not modify it; it will be overwritten |
|
2 |
# by rose_auto_create_model.pl automatically. |
|
3 |
package SL::DB::PriceRuleItem; |
|
4 |
|
|
5 |
use strict; |
|
6 |
|
|
7 |
use base qw(SL::DB::Object); |
|
8 |
|
|
9 |
__PACKAGE__->meta->table('price_rule_items'); |
|
10 |
|
|
11 |
__PACKAGE__->meta->columns( |
|
12 |
custom_variable_configs_id => { type => 'integer' }, |
|
13 |
id => { type => 'serial', not_null => 1 }, |
|
14 |
op => { type => 'text' }, |
|
15 |
price_rules_id => { type => 'integer', not_null => 1 }, |
|
16 |
type => { type => 'text' }, |
|
17 |
value_date => { type => 'date' }, |
|
18 |
value_int => { type => 'integer' }, |
|
19 |
value_num => { type => 'numeric', precision => 15, scale => 5 }, |
|
20 |
value_text => { type => 'text' }, |
|
21 |
); |
|
22 |
|
|
23 |
__PACKAGE__->meta->primary_key_columns([ 'id' ]); |
|
24 |
|
|
25 |
__PACKAGE__->meta->foreign_keys( |
|
26 |
custom_variable_configs => { |
|
27 |
class => 'SL::DB::CustomVariableConfig', |
|
28 |
key_columns => { custom_variable_configs_id => 'id' }, |
|
29 |
}, |
|
30 |
|
|
31 |
price_rules => { |
|
32 |
class => 'SL::DB::PriceRule', |
|
33 |
key_columns => { price_rules_id => 'id' }, |
|
34 |
}, |
|
35 |
); |
|
36 |
|
|
37 |
1; |
|
38 |
; |
SL/DB/Order.pm | ||
---|---|---|
183 | 183 |
return $self->${ \ $number_method{$self->type} }(@_); |
184 | 184 |
} |
185 | 185 |
|
186 |
sub customervendor { |
|
187 |
$_[0]->is_sales ? $_[0]->customer : $_[0]->vendor; |
|
188 |
} |
|
189 |
|
|
186 | 190 |
sub date { |
187 | 191 |
goto &transdate; |
188 | 192 |
} |
SL/DB/PriceRule.pm | ||
---|---|---|
1 |
# This file has been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::PriceRule; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use SL::DB::MetaSetup::PriceRule; |
|
9 |
use SL::DB::Manager::PriceRule; |
|
10 |
use Rose::DB::Object::Helpers qw(clone_and_reset); |
|
11 |
use SL::Locale::String qw(t8); |
|
12 |
|
|
13 |
__PACKAGE__->meta->add_relationship( |
|
14 |
items => { |
|
15 |
type => 'one to many', |
|
16 |
class => 'SL::DB::PriceRuleItem', |
|
17 |
column_map => { id => 'price_rules_id' }, |
|
18 |
}, |
|
19 |
); |
|
20 |
|
|
21 |
__PACKAGE__->meta->initialize; |
|
22 |
|
|
23 |
use Rose::Object::MakeMethods::Generic ( |
|
24 |
'scalar --get_set_init' => [ qw(price_or_discount_state) ], |
|
25 |
); |
|
26 |
|
|
27 |
sub match { |
|
28 |
my ($self, %params) = @_; |
|
29 |
|
|
30 |
die 'need record' unless $params{record}; |
|
31 |
die 'need record_item' unless $params{record_item}; |
|
32 |
|
|
33 |
for ($self->items) { |
|
34 |
next if $_->match(%params); |
|
35 |
# TODO save for error |
|
36 |
return |
|
37 |
} |
|
38 |
|
|
39 |
return 1; |
|
40 |
} |
|
41 |
|
|
42 |
sub is_sales { |
|
43 |
$_[0]->type eq 'customer' ? 1 |
|
44 |
: $_[0]->type eq 'vendor' ? 0 : do { die 'wrong type' }; |
|
45 |
} |
|
46 |
|
|
47 |
sub price_or_discount { |
|
48 |
my ($self, $value) = @_; |
|
49 |
|
|
50 |
if (@_ > 1) { |
|
51 |
my $number = $self->price || $self->discount; |
|
52 |
if ($value) { |
|
53 |
$self->discount($number); |
|
54 |
} else { |
|
55 |
$self->price($number); |
|
56 |
} |
|
57 |
$self->price_or_discount_state($value); |
|
58 |
} |
|
59 |
$self->price_or_discount_state; |
|
60 |
} |
|
61 |
|
|
62 |
sub price_or_discount_as_number { |
|
63 |
my ($self, @slurp) = @_; |
|
64 |
|
|
65 |
$self->price_or_discount ? $self->price(undef) : $self->discount(undef); |
|
66 |
$self->price_or_discount ? $self->discount_as_number(@slurp) : $self->price_as_number(@slurp); |
|
67 |
} |
|
68 |
|
|
69 |
sub init_price_or_discount_state { |
|
70 |
defined $_[0]->price ? 0 |
|
71 |
: defined $_[0]->discount ? 1 : 0 |
|
72 |
} |
|
73 |
|
|
74 |
sub validate { |
|
75 |
my ($self) = @_; |
|
76 |
|
|
77 |
my @errors; |
|
78 |
push @errors, $::locale->text('The name must not be empty.') if !$self->name; |
|
79 |
push @errors, $::locale->text('Price or discount must not be zero.') if !$self->price && !$self->discount; |
|
80 |
|
|
81 |
return @errors; |
|
82 |
} |
|
83 |
|
|
84 |
sub clone_and_reset_deep { |
|
85 |
my ($self) = @_; |
|
86 |
|
|
87 |
my $clone = $self->clone_and_reset; |
|
88 |
$clone->items(map { $_->clone_and_reset } $self->items); |
|
89 |
$clone->name(''); |
|
90 |
|
|
91 |
return $clone; |
|
92 |
} |
|
93 |
|
|
94 |
sub full_description { |
|
95 |
my ($self) = @_; |
|
96 |
|
|
97 |
my $items = join ', ', map { $_->full_description } $self->items; |
|
98 |
my $price = $self->price_or_discount |
|
99 |
? t8('Discount #1%', $self->discount_as_number) |
|
100 |
: t8('Price #1', $self->price_as_number); |
|
101 |
|
|
102 |
sprintf "%s: %s (%s)", $self->name, $price, $items; |
|
103 |
} |
|
104 |
|
|
105 |
sub in_use { |
|
106 |
my ($self) = @_; |
|
107 |
|
|
108 |
# is use is in this case used by record_items for their current price source |
|
109 |
# so, get any of those that might have it |
|
110 |
require SL::DB::OrderItem; |
|
111 |
require SL::DB::DeliveryOrderItem; |
|
112 |
require SL::DB::InvoiceItem; |
|
113 |
|
|
114 |
my $price_source_spec = 'price_rules' . '/' . $self->id; |
|
115 |
|
|
116 |
SL::DB::Manager::OrderItem->get_all_count(query => [ active_price_source => $price_source_spec ]) |
|
117 |
|| SL::DB::Manager::DeliveryOrderItem->get_all_count(query => [ active_price_source => $price_source_spec ]) |
|
118 |
|| SL::DB::Manager::InvoiceItem->get_all_count(query => [ active_price_source => $price_source_spec ]); |
|
119 |
} |
|
120 |
|
|
121 |
|
|
122 |
1; |
SL/DB/PriceRuleItem.pm | ||
---|---|---|
1 |
# This file has been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::PriceRuleItem; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use SL::DB::MetaSetup::PriceRuleItem; |
|
9 |
use SL::DB::Manager::PriceRuleItem; |
|
10 |
use Rose::DB::Object::Helpers qw(clone_and_reset); |
|
11 |
use SL::Locale::String qw(t8); |
|
12 |
|
|
13 |
__PACKAGE__->meta->initialize; |
|
14 |
|
|
15 |
use Rose::Object::MakeMethods::Generic ( |
|
16 |
'scalar --get_set_init' => [ qw(object operator) ], |
|
17 |
); |
|
18 |
|
|
19 |
sub match { |
|
20 |
my ($self, %params) = @_; |
|
21 |
|
|
22 |
die 'need record' unless $params{record}; |
|
23 |
die 'need record_item' unless $params{record_item}; |
|
24 |
|
|
25 |
$self->${\ "match_" . $self->type }(%params); |
|
26 |
} |
|
27 |
|
|
28 |
sub match_customer { |
|
29 |
$_[0]->value_int == $_[1]{record}->customer_id; |
|
30 |
} |
|
31 |
sub match_vendor { |
|
32 |
$_[0]->value_int == $_[1]{record}->vendor_id; |
|
33 |
} |
|
34 |
sub match_business { |
|
35 |
$_[0]->value_int == $_[1]{record}->customervendor->business_id; |
|
36 |
} |
|
37 |
sub match_partsgroup { |
|
38 |
$_[0]->value_int == $_[1]{record_item}->parts->partsgroup_id; |
|
39 |
} |
|
40 |
sub match_qty { |
|
41 |
if ($_[0]->op eq 'eq') { |
|
42 |
return $_[0]->value_num == $_[1]{record_item}->qty |
|
43 |
} elsif ($_[0]->op eq 'lt') { |
|
44 |
return $_[0]->value_num < $_[1]{record_item}->qty; |
|
45 |
} elsif ($_[0]->op eq 'gt') { |
|
46 |
return $_[0]->value_num > $_[1]{record_item}->qty; |
|
47 |
} |
|
48 |
} |
|
49 |
sub match_reqdate { |
|
50 |
if ($_[0]->op eq 'eq') { |
|
51 |
return $_[0]->value_date == $_[1]{record}->reqdate; |
|
52 |
} elsif ($_[0]->op eq 'lt') { |
|
53 |
return $_[0]->value_date < $_[1]{record}->reqdate; |
|
54 |
} elsif ($_[0]->op eq 'gt') { |
|
55 |
return $_[0]->value_date > $_[1]{record}->reqdate; |
|
56 |
} |
|
57 |
} |
|
58 |
sub match_pricegroup { |
|
59 |
$_[0]->value_int == $_[1]{record_item}->customervendor->pricegroup_id; |
|
60 |
} |
|
61 |
|
|
62 |
sub customer { |
|
63 |
require SL::DB::Customer; |
|
64 |
SL::DB::Customer->load_cached($_[0]->value_int); |
|
65 |
} |
|
66 |
|
|
67 |
sub vendor { |
|
68 |
require SL::DB::Vendor; |
|
69 |
SL::DB::Vendor->load_cached($_[0]->value_int); |
|
70 |
} |
|
71 |
|
|
72 |
sub business { |
|
73 |
require SL::DB::Business; |
|
74 |
SL::DB::Business->load_cached($_[0]->value_int); |
|
75 |
} |
|
76 |
|
|
77 |
sub partsgroup { |
|
78 |
require SL::DB::PartsGroup; |
|
79 |
SL::DB::PartsGroup->load_cached($_[0]->value_int); |
|
80 |
} |
|
81 |
|
|
82 |
sub pricegroup { |
|
83 |
require SL::DB::Pricegroup; |
|
84 |
SL::DB::Pricegroup->load_cached($_[0]->value_int); |
|
85 |
} |
|
86 |
|
|
87 |
sub full_description { |
|
88 |
my ($self) = @_; |
|
89 |
|
|
90 |
my $type = $self->type; |
|
91 |
my $op = $self->op; |
|
92 |
|
|
93 |
$type eq 'customer' ? t8('Customer') . ' ' . $self->customer->displayable_name |
|
94 |
: $type eq 'vendor' ? t8('Vendor') . ' ' . $self->vendor->displayable_name |
|
95 |
: $type eq 'business' ? t8('Type of Business') . ' ' . $self->business->displayable_name |
|
96 |
: $type eq 'partsgroup' ? t8('Group') . ' ' . $self->partsgroup->displayable_name |
|
97 |
: $type eq 'pricegroup' ? t8('Pricegroup') . ' ' . $self->pricegroup->displayable_name |
|
98 |
: $type eq 'qty' ? ( |
|
99 |
$op eq 'eq' ? t8('Qty equals #1', $self->value_num_as_number) |
|
100 |
: $op eq 'lt' ? t8('Qty less than #1', $self->value_num_as_number) |
|
101 |
: $op eq 'gt' ? t8('Qty more than #1', $self->value_num_as_number) |
|
102 |
: do { die "unknown op $op for type $type" } ) |
|
103 |
: $type eq 'reqdate' ? ( |
|
104 |
$op eq 'eq' ? t8('Reqdate is #1', $self->value_date_as_date) |
|
105 |
: $op eq 'lt' ? t8('Reqdate is before #1', $self->value_date_as_date) |
|
106 |
: $op eq 'gt' ? t8('Reqdate is after #1', $self->value_date_as_date) |
|
107 |
: do { die "unknown op $op for type $type" } ) |
|
108 |
: do { die "unknown type $type" } |
|
109 |
} |
|
110 |
|
|
111 |
1; |
SL/PriceSource/ALL.pm | ||
---|---|---|
7 | 7 |
use SL::PriceSource::Customer; |
8 | 8 |
use SL::PriceSource::Vendor; |
9 | 9 |
use SL::PriceSource::Business; |
10 |
use SL::PriceSource::PriceRules; |
|
10 | 11 |
|
11 | 12 |
my %price_sources_by_name = ( |
12 | 13 |
master_data => 'SL::PriceSource::MasterData', |
... | ... | |
15 | 16 |
pricegroup => 'SL::PriceSource::Pricegroup', |
16 | 17 |
makemodel => 'SL::PriceSource::Makemodel', |
17 | 18 |
business => 'SL::PriceSource::Business', |
19 |
price_rules => 'SL::PriceSource::PriceRules', |
|
18 | 20 |
); |
19 | 21 |
|
20 | 22 |
my @price_sources_order = qw( |
... | ... | |
24 | 26 |
pricegroup |
25 | 27 |
makemodel |
26 | 28 |
business |
29 |
price_rules |
|
27 | 30 |
); |
28 | 31 |
|
29 | 32 |
sub all_enabled_price_sources { |
SL/PriceSource/PriceRules.pm | ||
---|---|---|
1 |
package SL::PriceSource::PriceRules; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::PriceSource::Base); |
|
5 |
|
|
6 |
use SL::PriceSource::Price; |
|
7 |
use SL::Locale::String; |
|
8 |
use SL::DB::PriceRule; |
|
9 |
use List::UtilsBy qw(min_by max_by); |
|
10 |
|
|
11 |
sub name { 'price_rules' } |
|
12 |
|
|
13 |
sub description { t8('Price Rules') } |
|
14 |
|
|
15 |
sub available_rules { |
|
16 |
my ($self, %params) = @_; |
|
17 |
|
|
18 |
SL::DB::Manager::PriceRule->get_all_matching(record => $self->record, record_item => $self->record_item); |
|
19 |
} |
|
20 |
|
|
21 |
sub available_prices { |
|
22 |
my ($self, %params) = @_; |
|
23 |
|
|
24 |
my $rules = $self->available_rules; |
|
25 |
|
|
26 |
map { $self->make_price_from_rule($_) } @$rules; |
|
27 |
} |
|
28 |
|
|
29 |
sub price_from_source { |
|
30 |
my ($self, $source, $spec) = @_; |
|
31 |
|
|
32 |
my $rule = SL::DB::Manager::PriceRule->find_by(id => $spec); |
|
33 |
$self->make_price_from_rule($rule); |
|
34 |
} |
|
35 |
|
|
36 |
sub best_price { |
|
37 |
my ($self) = @_; |
|
38 |
|
|
39 |
$self->make_price_from_rule( min_by { $self->price_for_rule($_) } max_by { $_->priority } @{ $self->available_rules }); |
|
40 |
} |
|
41 |
|
|
42 |
sub price_for_rule { |
|
43 |
my ($self, $rule) = @_; |
|
44 |
$rule->price_or_discount |
|
45 |
? (1 - $rule->discount / 100) * ($rule->is_sales ? $self->part->sellprice : $self->part->lastcost) |
|
46 |
: $_->price; |
|
47 |
} |
|
48 |
|
|
49 |
sub make_price_from_rule { |
|
50 |
my ($self, $rule) = @_; |
|
51 |
|
|
52 |
SL::PriceSource::Price->new( |
|
53 |
price => $self->price_for_rule($rule), |
|
54 |
spec => $rule->id, |
|
55 |
description => $rule->name, |
|
56 |
price_source => $self, |
|
57 |
) |
|
58 |
} |
|
59 |
|
|
60 |
1; |
js/kivi.PriceRule.js | ||
---|---|---|
1 |
namespace('kivi.PriceRule', function(ns) { |
|
2 |
|
|
3 |
ns.add_new_row = function (type) { |
|
4 |
var data = { |
|
5 |
action: 'PriceRule/add_item_row', |
|
6 |
type: type |
|
7 |
}; |
|
8 |
$.post('controller.pl', data, kivi.eval_json_result); |
|
9 |
} |
|
10 |
|
|
11 |
$(function() { |
|
12 |
$('#price_rule_item_add').click(function() { |
|
13 |
ns.add_new_row($('#price_rules_empty_item_select').val()); |
|
14 |
}); |
|
15 |
$('#price_rule_items').on('click', 'a.price_rule_remove_line', function(){ |
|
16 |
$(this).closest('div').remove(); |
|
17 |
}) |
|
18 |
}); |
|
19 |
}); |
locale/de/all | ||
---|---|---|
176 | 176 |
'Add links' => 'Verknüpfungen hinzufügen', |
177 | 177 |
'Add new currency' => 'Neue Währung hinzufügen', |
178 | 178 |
'Add new custom variable' => 'Neue benutzerdefinierte Variable erfassen', |
179 |
'Add new price rule item' => 'Neue Bedingung hinzufügen', |
|
179 | 180 |
'Add note' => 'Notiz erfassen', |
180 | 181 |
'Add part' => 'Artikel hinzufügen', |
181 | 182 |
'Add picture' => 'Bild hinzufügen', |
... | ... | |
588 | 589 |
'Create a new group' => 'Neue Benutzergruppe erfassen', |
589 | 590 |
'Create a new payment term' => 'Neue Zahlungsbedingungen anlegen', |
590 | 591 |
'Create a new predefined text' => 'Einen neuen vordefinierten Textblock anlegen', |
592 |
'Create a new price rule' => 'Neue Preisregel anlegen', |
|
591 | 593 |
'Create a new printer' => 'Einen neuen Drucker anlegen', |
592 | 594 |
'Create a new project' => 'Neues Projekt anlegen', |
593 | 595 |
'Create a new project and link to it.' => 'Neues Projekt anlegen und damit verknüpfen.', |
... | ... | |
842 | 844 |
'Discard duplicate entries in CSV file' => 'Doppelte Einträge in CSV-Datei verwerfen', |
843 | 845 |
'Discard entries with duplicates in database or CSV file' => 'Einträge aus CSV-Datei verwerfen, die es bereits in der Datenbank oder der CSV-Datei gibt', |
844 | 846 |
'Discount' => 'Rabatt', |
847 |
'Discount #1%' => 'Rabatt #1%', |
|
845 | 848 |
'Discounts' => 'Rabatte', |
846 | 849 |
'Display' => 'Anzeigen', |
847 | 850 |
'Display file' => 'Datei anzeigen', |
... | ... | |
979 | 982 |
'Edit payment term' => 'Zahlungsbedingungen bearbeiten', |
980 | 983 |
'Edit picture' => 'Bild bearbeiten', |
981 | 984 |
'Edit predefined text' => 'Vordefinierten Textblock bearbeiten', |
985 |
'Edit price rule' => 'Preisregel bearbeiten', |
|
982 | 986 |
'Edit prices and discount (if not used, textfield is ONLY set readonly)' => 'Preise und Rabatt in Formularen frei anpassen (falls deaktiviert, wird allerdings NUR das textfield auf READONLY gesetzt / kann je nach Browserversion und technischen Fähigkeiten des Anwenders noch umgangen werden)', |
983 | 987 |
'Edit project' => 'Projekt bearbeiten', |
984 | 988 |
'Edit project #1' => 'Projekt #1 bearbeiten', |
... | ... | |
1231 | 1235 |
'II' => 'II', |
1232 | 1236 |
'III' => 'III', |
1233 | 1237 |
'IV' => 'IV', |
1238 |
'If all of the following match' => 'Wenn alle der folgenden Bedingungen zutreffen', |
|
1234 | 1239 |
'If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.' => 'Weichen die Beträge mehr als die "maximale Betragsabweichung" (siehe Einstellungen) ab, so wird diese Position als ungültig markiert.', |
1235 | 1240 |
'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => 'Falls angehakt wird der DATEV-Steuerschlüssel bei Buchungen auf dieses Konto nicht beim DATEV-Export mitexportiert, allerdings nur wenn zusätzlich der Konto-Steuerschlüssel vom Buchungs (Hauptbuch) Steuerschlüssel abweicht', |
1236 | 1241 |
'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => 'Falls konfiguriert, wird dieses Lager mit Lagerplatz für neu angelegte Waren vorausgewählt.', |
... | ... | |
1527 | 1532 |
'Net amount (for verification)' => 'Nettobetrag (zur Überprüfung)', |
1528 | 1533 |
'Netto Terms' => 'Zahlungsziel netto', |
1529 | 1534 |
'New Password' => 'Neues Passwort', |
1535 |
'New Price Rule' => 'Neue Preisregel', |
|
1530 | 1536 |
'New assembly' => 'Neues Erzeugnis', |
1531 | 1537 |
'New bank account' => 'Neues Bankkonto', |
1532 | 1538 |
'New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.' => 'Neuer Mandant #1: Die Datenbankkonfigurationsfelder "Host", "Port" und "Name" dürfen nicht leer sein.', |
... | ... | |
1846 | 1852 |
'Previous transdate text' => 'wurde gespeichert am', |
1847 | 1853 |
'Previous transnumber text' => 'Letzte Buchung mit der Buchungsnummer', |
1848 | 1854 |
'Price' => 'Preis', |
1855 |
'Price #1' => 'Preis #1', |
|
1849 | 1856 |
'Price Factor' => 'Preisfaktor', |
1850 | 1857 |
'Price Factors' => 'Preisfaktoren', |
1858 |
'Price Rules' => 'Preisregeln', |
|
1851 | 1859 |
'Price Source' => 'Preisquelle', |
1852 | 1860 |
'Price Sources to be disabled in this client' => 'Preisquellen die in diesem Mandanten deaktiviert werden sollen', |
1853 | 1861 |
'Price factor (database ID)' => 'Preisfaktor (Datenbank-ID)', |
... | ... | |
1857 | 1865 |
'Price group (database ID)' => 'Preisgruppe (Datenbank-ID)', |
1858 | 1866 |
'Price group (name)' => 'Preisgruppe (Name) ', |
1859 | 1867 |
'Price information' => 'Preisinformation', |
1868 |
'Price or discount must not be zero.' => 'Preis/Rabatt darf nicht 0,00 sein', |
|
1860 | 1869 |
'Price sources deactivated in this client' => 'Preisquellen die in diesem Mandanten deaktiviert sind', |
1861 | 1870 |
'Pricegroup' => 'Preisgruppe', |
1862 | 1871 |
'Pricegroup deleted!' => 'Preisgruppe gelöscht!', |
... | ... | |
1880 | 1889 |
'Printer management' => 'Druckerverwaltung', |
1881 | 1890 |
'Printing ... ' => 'Es wird gedruckt.', |
1882 | 1891 |
'Prior year' => 'Vorheriges Jahr', |
1892 |
'Priority' => 'Priorität', |
|
1883 | 1893 |
'Private E-mail' => 'Private E-Mail', |
1884 | 1894 |
'Private Phone' => 'Privates Tel.', |
1885 | 1895 |
'Problem' => 'Problem', |
... | ... | |
1905 | 1915 |
'Projects' => 'Projekte', |
1906 | 1916 |
'Projecttransactions' => 'Projektbuchungen', |
1907 | 1917 |
'Prozentual/Absolut' => 'Prozentual/Absolut', |
1918 |
'Purchase' => '', |
|
1908 | 1919 |
'Purchase Delivery Orders' => 'Einkaufslieferscheine', |
1909 | 1920 |
'Purchase Delivery Orders deleteable' => 'Einkaufslieferscheine löschbar', |
1910 | 1921 |
'Purchase Invoice' => 'Einkaufsrechnung', |
... | ... | |
1913 | 1924 |
'Purchase Orders' => 'Lieferantenaufträge', |
1914 | 1925 |
'Purchase Orders deleteable' => 'Lieferantenaufträge löschbar', |
1915 | 1926 |
'Purchase Price' => 'Einkaufspreis', |
1927 |
'Purchase Price Rules ' => 'Preisregeln (Einkauf)', |
|
1916 | 1928 |
'Purchase Prices' => 'Einkaufspreise', |
1917 | 1929 |
'Purchase delivery order' => 'Lieferschein (Einkauf)', |
1918 | 1930 |
'Purchase invoices' => 'Einkaufsrechnungen', |
... | ... | |
1924 | 1936 |
'Purpose' => 'Verwendungszweck', |
1925 | 1937 |
'Qty' => 'Menge', |
1926 | 1938 |
'Qty according to delivery order' => 'Menge laut Lieferschein', |
1939 |
'Qty equals #1' => 'Menge ist #1', |
|
1927 | 1940 |
'Qty in Selected Records' => 'Menge in gewählten Belegen', |
1928 | 1941 |
'Qty in stock' => 'Lagerbestand', |
1942 |
'Qty less than #1' => 'Menge weniger als #1', |
|
1943 |
'Qty more than #1' => 'Menge mehr als #1', |
|
1929 | 1944 |
'Quantity' => 'Menge', |
1930 | 1945 |
'Quantity missing.' => 'Die Mengenangabe fehlt.', |
1931 | 1946 |
'Quartal' => 'Quartal', |
... | ... | |
2000 | 2015 |
'Representative' => 'Vertreter', |
2001 | 2016 |
'Representative for Customer' => 'Vertreter für Kunden', |
2002 | 2017 |
'Reqdate' => 'Liefertermin', |
2018 |
'Reqdate is #1' => 'Liefertermin ist #1', |
|
2019 |
'Reqdate is after #1' => 'Liefertermin nach #1', |
|
2020 |
'Reqdate is before #1' => 'Liefertermin vor #1', |
|
2003 | 2021 |
'Reqdate not set or before current month' => 'Lieferdatum nicht gesetzt oder vor aktuellem Monat', |
2004 | 2022 |
'Request Quotations' => 'Preisanfragen', |
2005 | 2023 |
'Request for Quotation' => 'Anfrage', |
... | ... | |
2059 | 2077 |
'Saldo neu' => 'Saldo neu', |
2060 | 2078 |
'Saldo per' => 'Saldo per', |
2061 | 2079 |
'Sale Prices' => 'Verkaufspreise', |
2080 |
'Sales' => '', |
|
2062 | 2081 |
'Sales Delivery Orders' => 'Verkaufslieferscheine', |
2063 | 2082 |
'Sales Delivery Orders deleteable' => 'Verkaufslieferscheine löschbar', |
2064 | 2083 |
'Sales Invoice' => 'Rechnung', |
... | ... | |
2066 | 2085 |
'Sales Order' => 'Kundenauftrag', |
2067 | 2086 |
'Sales Orders' => 'Aufträge', |
2068 | 2087 |
'Sales Orders deleteable' => 'Kundenaufträge löschbar', |
2088 |
'Sales Price Rules ' => 'Preisregeln (Verkauf)', |
|
2069 | 2089 |
'Sales Price information' => 'Verkaufspreisinformation', |
2070 | 2090 |
'Sales Quotation valid interval' => 'Angebotsgültigkeitsintervall', |
2071 | 2091 |
'Sales Quotations' => 'Angebote', |
... | ... | |
2166 | 2186 |
'Service Number missing!' => 'Dienstleistungsnummer fehlt!', |
2167 | 2187 |
'Service, assembly or part' => 'Dienstleistung, Erzeugnis oder Ware', |
2168 | 2188 |
'Services' => 'Dienstleistungen', |
2189 |
'Set (set to)' => 'Setze', |
|
2169 | 2190 |
'Set eMail text' => 'E-Mail Text eingeben', |
2170 | 2191 |
'Settings' => 'Einstellungen', |
2171 | 2192 |
'Setup Menu' => 'Menü-Variante', |
... | ... | |
2510 | 2531 |
'The name is missing in row %d.' => 'Der Name fehlt in Zeile %d.', |
2511 | 2532 |
'The name is missing.' => 'Der Name fehlt.', |
2512 | 2533 |
'The name is not unique.' => 'Der Name ist nicht eindeutig.', |
2534 |
'The name must not be empty.' => 'Der Name darf nicht leer sein.', |
|
2513 | 2535 |
'The name must only consist of letters, numbers and underscores and start with a letter.' => 'Der Name darf nur aus Buchstaben (keine Umlaute), Ziffern und Unterstrichen bestehen und muss mit einem Buchstaben beginnen.', |
2514 | 2536 |
'The new requirement spec template will be a copy of \'#1\'.' => 'Die neue Pflichtenheftvorlage wird eine Kopie von \'#1\' sein.', |
2515 | 2537 |
'The new requirement spec will be a copy of \'#1\' for customer \'#2\'.' => 'Das neue Pflichtenheft wird eine Kopie von \'#1\' für Kunde \'#2\' sein.', |
... | ... | |
2535 | 2557 |
'The predefined text has been saved.' => 'Der vordefinierte Textblock wurde gespeichert.', |
2536 | 2558 |
'The predefined text is in use and cannot be deleted.' => 'Der vordefinierte Textblock wird verwendet und kann nicht gelöscht werden.', |
2537 | 2559 |
'The preferred one is to install packages provided by your operating system distribution (e.g. Debian or RPM packages).' => 'Die bevorzugte Art, ein Perl-Modul zu installieren, ist durch Installation eines von Ihrem Betriebssystem zur Verfügung gestellten Paketes (z.B. Debian-Pakete oder RPM).', |
2560 |
'The price rule has been created.' => 'Die Preisregel wurde angelegt.', |
|
2561 |
'The price rule has been obsoleted.' => 'Diese Preisregel ist nicht mehr gültig', |
|
2562 |
'The price rule has been saved.' => 'Die preisregel wurde gespeichert.', |
|
2538 | 2563 |
'The printer could not be deleted.' => 'Der Drucker konnte nicht gelöscht werden.', |
2539 | 2564 |
'The printer has been created.' => 'Der Drucker wurde angelegt.', |
2540 | 2565 |
'The printer has been deleted.' => 'Der Drucker wurde entfernt.', |
... | ... | |
2627 | 2652 |
'The wrong taxkeys for AP and AR transactions have been fixed.' => 'Die Probleme mit falschen Steuerschlüssel bei Kreditoren- und Debitorenbuchungen wurden behoben.', |
2628 | 2653 |
'The wrong taxkeys for inventory transactions for sales and purchase invoices have been fixed.' => 'Die falschen Steuerschlüssel für Warenbestandsbuchungen bei Einkaufs- und Verkaufsrechnungen wurden behoben.', |
2629 | 2654 |
'The wrong taxkeys have been fixed.' => 'Die Steuerschlüssel wurden nach Ihrer Auswahl korrigiert.', |
2655 |
'Then' => 'Dann', |
|
2630 | 2656 |
'Then go to the database administration and chose "create database".' => 'Dann gehen Sie in den Datenbankverwaltung, und wählen Sie dort "Datenbank anlegen" aus.', |
2631 | 2657 |
'There are #1 more open invoices for this customer with other currencies.' => 'Es gibt #1 weitere offene Rechnungen für diesen Kunden, die in anderen Währungen ausgestellt wurden.', |
2632 | 2658 |
'There are #1 more open invoices from this vendor with other currencies.' => 'Es gibt #1 weitere offene Rechnungen von diesem Lieferanten, die in anderen Währungen ausgestellt wurden.', |
... | ... | |
3057 | 3083 |
'invoice' => 'Rechnung', |
3058 | 3084 |
'invoice mode or item mode' => 'Rechnungsmodus oder Artikelmodus', |
3059 | 3085 |
'invoice_list' => 'debitorenbuchungsliste', |
3086 |
'is' => 'ist', |
|
3087 |
'is after' => 'ist nach dem', |
|
3088 |
'is before' => 'ist vor dem', |
|
3089 |
'is equal to' => 'ist gleich', |
|
3090 |
'is greater than' => 'ist größer als', |
|
3091 |
'is lower than' => 'ist kleiner als', |
|
3060 | 3092 |
'kivitendo' => 'kivitendo', |
3061 | 3093 |
'kivitendo Homepage' => 'Infos zu kivitendo', |
3062 | 3094 |
'kivitendo can fix these problems automatically.' => 'kivitendo kann solche Probleme automatisch beheben.', |
... | ... | |
3166 | 3198 |
'terminated' => 'gekündigt', |
3167 | 3199 |
'time and effort based position' => 'Aufwandsposition', |
3168 | 3200 |
'to (date)' => 'bis', |
3201 |
'to (set to)' => 'auf', |
|
3169 | 3202 |
'to (time)' => 'bis', |
3170 | 3203 |
'transfer' => 'Umlagerung', |
3171 | 3204 |
'transferred in' => 'eingelagert', |
menus/erp.ini | ||
---|---|---|
46 | 46 |
module=ic.pl |
47 | 47 |
action=search_update_prices |
48 | 48 |
|
49 |
[Master Data--Sales Price Rules ] |
|
50 |
ACCESS=part_service_assembly_edit |
|
51 |
module=controller.pl |
|
52 |
action=PriceRule/list |
|
53 |
filter.type=customer |
|
54 |
|
|
55 |
[Master Data--Purchase Price Rules ] |
|
56 |
ACCESS=part_service_assembly_edit |
|
57 |
module=controller.pl |
|
58 |
action=PriceRule/list |
|
59 |
filter.type=vendor |
|
49 | 60 |
|
50 | 61 |
[Master Data--Reports] |
51 | 62 |
module=menu.pl |
sql/Pg-upgrade2/price_rules.sql | ||
---|---|---|
1 |
-- @tag: price_rules |
|
2 |
-- @description: Preismatrix Tabellen |
|
3 |
-- @depends: release_3_1_0 |
|
4 |
-- @encoding: utf-8 |
|
5 |
|
|
6 |
CREATE TABLE price_rules ( |
|
7 |
id SERIAL PRIMARY KEY, |
|
8 |
name TEXT, |
|
9 |
type TEXT, |
|
10 |
priority INTEGER NOT NULL DEFAULT 3, |
|
11 |
price NUMERIC(15,5), |
|
12 |
discount NUMERIC(15,5), |
|
13 |
obsolete BOOLEAN NOT NULL DEFAULT FALSE, |
|
14 |
itime TIMESTAMP, |
|
15 |
mtime TIMESTAMP |
|
16 |
); |
|
17 |
|
|
18 |
CREATE TABLE price_rule_items ( |
|
19 |
id SERIAL PRIMARY KEY, |
|
20 |
price_rules_id INTEGER NOT NULL, |
|
21 |
type TEXT, |
|
22 |
op TEXT, |
|
23 |
custom_variable_configs_id INTEGER, |
|
24 |
value_text TEXT, |
|
25 |
value_int INTEGER, |
|
26 |
value_date DATE, |
|
27 |
value_num NUMERIC(15,5), |
|
28 |
itime TIMESTAMP, |
|
29 |
mtime TIMESTAMP, |
|
30 |
FOREIGN KEY (price_rules_id) REFERENCES price_rules (id), |
|
31 |
FOREIGN KEY (custom_variable_configs_id) REFERENCES custom_variable_configs (id) |
|
32 |
); |
|
33 |
|
|
34 |
CREATE TRIGGER mtime_price_rules BEFORE UPDATE ON price_rules FOR EACH ROW EXECUTE PROCEDURE set_mtime(); |
|
35 |
CREATE TRIGGER mtime_price_rule_items BEFORE UPDATE ON price_rule_items FOR EACH ROW EXECUTE PROCEDURE set_mtime(); |
templates/webpages/price_rule/_filter.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE L %] |
|
3 |
[%- USE LxERP %] |
|
4 |
[%- USE HTML %] |
|
5 |
<form action='controller.pl' method='post'> |
|
6 |
<div class='filter_toggle'> |
|
7 |
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a> |
|
8 |
[% SELF.filter_summary | html %] |
|
9 |
</div> |
|
10 |
<div class='filter_toggle' style='display:none'> |
|
11 |
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a> |
|
12 |
<table id='filter_table'> |
|
13 |
<tr> |
|
14 |
<tr> |
|
15 |
<th align="right">[% 'Description' | $T8 %]</th> |
|
16 |
<td>[% L.input_tag('filter.name:substr::ilike', filter.name_substr__ilike, size = 20) %]</td> |
|
17 |
</tr> |
|
18 |
<tr> |
|
19 |
<th align="right">[% 'Price' | $T8 %]</th> |
|
20 |
<td>[% L.input_tag('filter.price:number', filter.price_number, size=20) %]</td> |
|
21 |
</tr> |
|
22 |
<tr> |
|
23 |
<th align="right">[% 'Discount' | $T8 %]</th> |
|
24 |
<td>[% L.input_tag('filter.discount:number', filter.discount_number, size=20) %]</td> |
|
25 |
</tr> |
|
26 |
</table> |
|
27 |
|
|
28 |
[% L.hidden_tag('action', 'PriceRule/dispatch') %] |
|
29 |
[% L.hidden_tag('filter.type', FORM.filter.type) %] |
|
30 |
[% L.hidden_tag('sort_by', FORM.sort_by) %] |
|
31 |
[% L.hidden_tag('sort_dir', FORM.sort_dir) %] |
|
32 |
[% L.hidden_tag('page', FORM.page) %] |
|
33 |
[% L.input_tag('action_list', LxERP.t8('Continue'), type = 'submit', class='submit')%] |
|
34 |
|
|
35 |
<a href='#' onClick='javascript:$("#filter_table input").val("");$("#filter_table input[type=checkbox]").prop("checked", 0);'>[% 'Reset' | $T8 %]</a> |
|
36 |
|
|
37 |
</div> |
|
38 |
|
|
39 |
</form> |
templates/webpages/price_rule/empty_item.html | ||
---|---|---|
1 |
[%- USE L %] |
|
2 |
[%- USE T8 %] |
|
3 |
[% L.select_tag('', SELF.all_price_rule_item_types, id='price_rules_empty_item_select') %] |
|
4 |
<a id='price_rule_item_add'>[% 'Add new price rule item' | $T8 %]</a> |
templates/webpages/price_rule/form.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE L %][%- USE P %] |
|
3 |
[%- USE HTML %][%- USE LxERP %] |
|
4 |
|
|
5 |
<h1>[% title %]</h1> |
|
6 |
|
|
7 |
[%- INCLUDE 'common/flash.html' %] |
|
8 |
|
|
9 |
<form method="post" action="controller.pl"> |
|
10 |
[% L.hidden_tag("price_rule.id", SELF.price_rule.id) %] |
|
11 |
[% L.hidden_tag("price_rule.type", SELF.price_rule.type) %] |
|
12 |
|
|
13 |
<table> |
|
14 |
<tr> |
|
15 |
<th align="right">[% 'Name' | $T8 %]</th> |
|
16 |
<td>[% L.input_tag("price_rule.name", SELF.price_rule.name, size=60, class='initial_focus') %]</td> |
|
17 |
</tr> |
|
18 |
[%- IF 0 %] |
|
19 |
<tr> |
|
20 |
<th align="right">[% 'Type' | $T8 %]</th> |
|
21 |
<td>[% L.select_tag("price_rule.type", [ [ 'sales', LxERP.t8('Sales')], [ 'purchase', LxERP.t8('Purchase') ] ], default=SELF.price_rule.type) %]</td> |
|
22 |
</tr> |
|
23 |
[%- END %] |
|
24 |
<tr> |
|
25 |
<th align="right">[% 'Priority' | $T8 %]</th> |
|
26 |
<td>[% L.select_tag('price_rule.priority', [1,2,3,4,5], default=SELF.price_rule.priority, style='width: 300px') %]</td> |
|
27 |
</tr> |
|
28 |
|
|
29 |
<tr> |
|
30 |
<th align="right">[% 'Valid' | $T8 %]</th> |
|
31 |
<td>[% L.select_tag('project.project_type_id', [ [ 0, LxERP.t8('Valid') ], [ 1 , LxERP.t8('Obsolete')]], default=SELF.price_rule.obsolete, title_key='description', style='width: 300px') %]</td> |
|
32 |
</tr> |
|
33 |
|
|
34 |
<tr> |
|
35 |
<tr> |
|
36 |
</table> |
|
37 |
|
|
38 |
<h3>[% 'If all of the following match' | $T8 %]:</h3> |
|
39 |
|
|
40 |
<div id='price_rule_items' style='margin-left: 20px;'> |
|
41 |
[% FOREACH item = SELF.price_rule.items %] |
|
42 |
[% PROCESS 'price_rule/item.html' item=item %] |
|
43 |
[% END %] |
|
44 |
<div id='price_rule_new_items'></div> |
|
45 |
<div>[% PROCESS 'price_rule/empty_item.html' %]</div> |
|
46 |
</div> |
|
47 |
|
|
48 |
<h3>[% 'Then' | $T8 %]:</h3> |
|
49 |
<div>[% 'Set (set to)' | $T8 %] [% L.select_tag('price_rule.price_or_discount', [ [0, LxERP.t8('Price') ], [1, LxERP.t8('Discount') ]], default=SELF.price_rule.price_or_discount) %] [% 'to (set to)' | $T8 %] [% L.input_tag('price_rule.price_or_discount_as_number', SELF.price_rule.price_or_discount_as_number) %] |
|
50 |
</div> |
|
51 |
|
|
52 |
<p> |
|
53 |
[% L.hidden_tag("action", "PriceRule/dispatch") %] |
|
54 |
[% L.hidden_tag("callback", FORM.callback) %] |
|
55 |
[% L.submit_tag("action_" _ (SELF.price_rule.id ? "update" : "create"), LxERP.t8('Save')) %] |
|
56 |
[%- IF SELF.price_rule.id %] |
|
57 |
[% L.submit_tag("action_create", LxERP.t8('Save as new')) %] |
|
58 |
[% L.submit_tag("action_destroy", LxERP.t8('Delete'), confirm=LxERP.t8('Do you really want to delete this object?')) IF !SELF.price_rule.in_use %] |
|
59 |
[%- END %] |
|
60 |
<a href="[% SELF.url_for(action='list', 'vc'=SELF.price_rule.type) %]">[%- LxERP.t8('Abort') %]</a> |
|
61 |
</p> |
|
62 |
</form> |
templates/webpages/price_rule/item.html | ||
---|---|---|
1 |
[%- USE L %] |
|
2 |
[%- USE HTML %] |
|
3 |
[%- USE T8 %] |
|
4 |
[%- USE LxERP %] |
|
5 |
[% SET num_compare_ops = [ |
|
6 |
[ 'eq', LxERP.t8('is equal to') ], |
|
7 |
[ 'lt', LxERP.t8('is lower than') ], |
|
8 |
[ 'gt', LxERP.t8('is greater than') ], |
|
9 |
] %] |
|
10 |
[% SET date_compare_ops = [ |
|
11 |
[ 'eq', LxERP.t8('is equal to') ], |
|
12 |
[ 'gt', LxERP.t8('is after') ], |
|
13 |
[ 'lt', LxERP.t8('is before') ], |
|
14 |
] %] |
|
15 |
<div> |
|
16 |
<a class='price_rule_remove_line'><img height="10px" width="10px" src="image/cross.png" alt="[% 'Remove' | $T8 %]"></a> |
|
17 |
[% L.hidden_tag('price_rule.items[+].id', item.id) %] |
|
18 |
[% L.hidden_tag('price_rule.items[].type', item.type) %] |
|
19 |
[%- SWITCH item.type %] |
|
20 |
[% CASE 'customer' %] |
|
21 |
[% 'Customer' | $T8 %] [% 'is' | $T8 %] [% L.customer_vendor_picker('price_rule.items[].value_int', item.customer, type='customer') %] |
|
22 |
[% CASE 'vendor' %] |
|
23 |
[% 'Vendor' | $T8 %] [% 'is' | $T8 %] [% L.vendor_vendor_picker('price_rule.items[].value_int', item.customer, type='vendor') %] |
|
24 |
[% CASE 'business' %] |
|
25 |
[% 'Type of Business' | $T8 %] [% 'is' | $T8 %] [% L.select_tag('price_rule.items[].value_int', SELF.businesses, title_key='description', default=item.value_int) %] |
|
26 |
[% CASE 'partsgroup' %] |
|
27 |
[% 'Group' | $T8 %] [% 'is' | $T8 %] [% L.select_tag('price_rule.items[].value_int', SELF.partsgroups, title_key='partsgroup', default=item.value_int) %] |
|
28 |
[% CASE 'qty' %] |
|
29 |
[% 'Quantity' | $T8 %] [% L.select_tag('price_rule.items[].op', num_compare_ops, default=item.op) %] [% L.input_tag('price_rule.items[].value_num_as_number', item.value_num_as_number) %] |
|
30 |
[% CASE 'reqdate' %] |
|
31 |
[% 'Reqdate' | $T8 %] [% L.select_tag('price_rule.items[].op', date_compare_ops, default=item.op) %] [% L.date_tag('price_rule.items[].value_date', item.value_date) %] |
|
32 |
[% CASE 'pricegroup' %] |
|
33 |
[% 'Pricegroup' | $T8 %] [% 'is' | $T8 %] [% L.select_tag('price_rule.items[].value_int', SELF.pricegroups, title_key='pricegroup', default=item.value_int) %] |
|
34 |
[% CASE %] |
|
35 |
[%- END %] |
|
36 |
</div> |
templates/webpages/price_rule/report_bottom.html | ||
---|---|---|
1 |
[% USE L %] |
|
2 |
[% USE T8 %] |
|
3 |
[% USE HTML %] |
|
4 |
[%- L.paginate_controls(models=SELF.models) %] |
|
5 |
|
|
6 |
<a href="[% SELF.url_for(action='new', 'price_rule.type'=SELF.vc, callback=SELF.models.get_callback) | html %]">[% 'New Price Rule' | $T8 %]</a> |
templates/webpages/price_rule/report_top.html | ||
---|---|---|
1 |
[%- USE L %] |
|
2 |
[%- PROCESS 'price_rule/_filter.html' filter=SELF.filter %] |
|
3 |
<hr> |
Auch abrufbar als: Unified diff
PriceRule: Erste Version