Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision da8f5ec2

Von Tamino Steinert vor fast 2 Jahren hinzugefügt

  • ID da8f5ec205081ca602c02403d559f7b68b2e9566
  • Vorgänger d58b1a04
  • Nachfolger 4660e100

Reclamation: sql-script for data tables and rose objects added

Also created all needed functions in SL/DB/Reclamation.pm,
SL/DB/ReclamationItem.pm and SL/DB/ReclamationReason.pm

Unterschiede anzeigen:

SL/DB/Helper/ALL.pm
103 103
use SL::DB::ProjectStatus;
104 104
use SL::DB::ProjectType;
105 105
use SL::DB::PurchaseInvoice;
106
use SL::DB::Reclamation;
107
use SL::DB::ReclamationItem;
108
use SL::DB::ReclamationReason;
106 109
use SL::DB::ReconciliationLink;
107 110
use SL::DB::RecordLink;
108 111
use SL::DB::RecordTemplate;
SL/DB/Helper/Mappings.pm
182 182
  project_roles                  => 'project_role',
183 183
  project_statuses               => 'project_status',
184 184
  project_types                  => 'project_type',
185
  reclamations                   => 'Reclamation',
186
  reclamation_items              => 'ReclamationItem',
187
  reclamation_reasons            => 'ReclamationReason',
185 188
  reconciliation_links           => 'reconciliation_link',
186 189
  record_links                   => 'record_link',
187 190
  record_templates               => 'record_template',
SL/DB/Manager/Reclamation.pm
1
package SL::DB::Manager::Reclamation;
2

  
3
use strict;
4

  
5
use parent qw(SL::DB::Helper::Manager);
6

  
7
use SL::DB::Helper::Paginated;
8
use SL::DB::Helper::Sorted;
9
use SL::DB::Helper::Filtered;
10

  
11
sub object_class { 'SL::DB::Reclamation' }
12

  
13
__PACKAGE__->make_manager_methods;
14

  
15
__PACKAGE__->add_filter_specs(
16
  type => sub {
17
    my ($key, $value, $prefix) = @_;
18
    return __PACKAGE__->type_filter($value, $prefix);
19
  },
20
  # todo when is this used?
21
  #all => sub {
22
  #  my ($key, $value, $prefix) = @_;
23
  #  return or => [ map { $prefix . $_ => $value } qw(record_number customer.name vendor.name transaction_description) ]
24
  #}
25
);
26

  
27
sub type_filter {
28
  my $class  = shift;
29
  my $type   = lc(shift || '');
30
  my $prefix = shift || '';
31

  
32
  return (and => [ "!customer_id" => undef ]) if $type eq 'sales_reclamation';
33
  return (and => [ "!vendor_id"   => undef ]) if $type eq 'purchase_reclamation';
34

  
35
  die "Unknown type $type";
36
}
37

  
38
sub _sort_spec {
39
  return (
40
    default                   => [ 'transdate', 1 ],
41
    nulls                     => {
42
      transaction_description => 'FIRST',
43
      default                 => 'LAST',
44
    },
45
    columns                   => {
46
      SIMPLE                  => 'ALL',
47
      customer                => 'lower(customer.name)',
48
      vendor                  => 'lower(vendor.name)',
49
      employee                => 'lower(employee.name)',
50
      globalprojectnumber     => 'lower(globalproject.projectnumber)',
51

  
52
      # Bug in Rose::DB::Object: the next should be
53
      # "globalproject.project_type.description". This workaround will
54
      # only work if no other table with "project_type" is visible in
55
      # the current query
56
      globalproject_type      => 'lower(project_type.description)',
57

  
58
      map { ( $_ => "lower(reclamations.$_)" ) }
59
        qw(record_number cv_record_number shippingpoint shipvia notes intnotes
60
           transaction_description
61
        ),
62
    });
63
}
64

  
65
1;
SL/DB/Manager/ReclamationItem.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::ReclamationItem;
5

  
6
use strict;
7

  
8
use parent qw(SL::DB::Helper::Manager);
9

  
10
sub object_class { 'SL::DB::ReclamationItem' }
11

  
12
__PACKAGE__->make_manager_methods;
13

  
14
1;
SL/DB/Manager/ReclamationReason.pm
1
package SL::DB::Manager::ReclamationReason;
2

  
3
use strict;
4

  
5
use parent qw(SL::DB::Helper::Manager);
6
use SL::DB::Helper::Paginated;
7
use SL::DB::Helper::Filtered;
8

  
9
use SL::DB::Helper::Sorted;
10

  
11
sub object_class { 'SL::DB::ReclamationReason' }
12

  
13
__PACKAGE__->make_manager_methods;
14
__PACKAGE__->add_filter_specs(
15
  all => sub {
16
    my ($key, $value, $prefix) = @_;
17
    return or => [ map { $prefix . $_ => $value } qw(reclamation_reason) ]
18
  },
19
);
20

  
21
sub _sort_spec {
22
  return ( default => [ 'position', 1 ],
23
           columns => { SIMPLE => 'ALL' });
24
}
25
1;
SL/DB/MetaSetup/Reclamation.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::Reclamation;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('reclamations');
10

  
11
__PACKAGE__->meta->columns(
12
  amount                  => { type => 'numeric', precision => 15, scale => 5 },
13
  closed                  => { type => 'boolean', default => 'false', not_null => 1 },
14
  contact_id              => { type => 'integer' },
15
  currency_id             => { type => 'integer', not_null => 1 },
16
  customer_id             => { type => 'integer' },
17
  cv_record_number        => { type => 'text' },
18
  delivered               => { type => 'boolean', default => 'false', not_null => 1 },
19
  delivery_term_id        => { type => 'integer' },
20
  department_id           => { type => 'integer' },
21
  employee_id             => { type => 'integer', not_null => 1 },
22
  exchangerate            => { type => 'numeric', precision => 15, scale => 5 },
23
  globalproject_id        => { type => 'integer' },
24
  id                      => { type => 'integer', not_null => 1, sequence => 'id' },
25
  intnotes                => { type => 'text' },
26
  itime                   => { type => 'timestamp', default => 'now()' },
27
  language_id             => { type => 'integer' },
28
  mtime                   => { type => 'timestamp' },
29
  netamount               => { type => 'numeric', precision => 15, scale => 5 },
30
  notes                   => { type => 'text' },
31
  payment_id              => { type => 'integer' },
32
  record_number           => { type => 'text', not_null => 1 },
33
  reqdate                 => { type => 'date' },
34
  salesman_id             => { type => 'integer' },
35
  shippingpoint           => { type => 'text' },
36
  shipto_id               => { type => 'integer' },
37
  shipvia                 => { type => 'text' },
38
  tax_point               => { type => 'date' },
39
  taxincluded             => { type => 'boolean', not_null => 1 },
40
  taxzone_id              => { type => 'integer', not_null => 1 },
41
  transaction_description => { type => 'text' },
42
  transdate               => { type => 'date', default => 'now()' },
43
  vendor_id               => { type => 'integer' },
44
);
45

  
46
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
47

  
48
__PACKAGE__->meta->allow_inline_column_values(1);
49

  
50
__PACKAGE__->meta->foreign_keys(
51
  contact => {
52
    class       => 'SL::DB::Contact',
53
    key_columns => { contact_id => 'cp_id' },
54
  },
55

  
56
  currency => {
57
    class       => 'SL::DB::Currency',
58
    key_columns => { currency_id => 'id' },
59
  },
60

  
61
  customer => {
62
    class       => 'SL::DB::Customer',
63
    key_columns => { customer_id => 'id' },
64
  },
65

  
66
  delivery_term => {
67
    class       => 'SL::DB::DeliveryTerm',
68
    key_columns => { delivery_term_id => 'id' },
69
  },
70

  
71
  department => {
72
    class       => 'SL::DB::Department',
73
    key_columns => { department_id => 'id' },
74
  },
75

  
76
  employee => {
77
    class       => 'SL::DB::Employee',
78
    key_columns => { employee_id => 'id' },
79
  },
80

  
81
  globalproject => {
82
    class       => 'SL::DB::Project',
83
    key_columns => { globalproject_id => 'id' },
84
  },
85

  
86
  language => {
87
    class       => 'SL::DB::Language',
88
    key_columns => { language_id => 'id' },
89
  },
90

  
91
  payment => {
92
    class       => 'SL::DB::PaymentTerm',
93
    key_columns => { payment_id => 'id' },
94
  },
95

  
96
  salesman => {
97
    class       => 'SL::DB::Employee',
98
    key_columns => { salesman_id => 'id' },
99
  },
100

  
101
  shipto => {
102
    class       => 'SL::DB::Shipto',
103
    key_columns => { shipto_id => 'shipto_id' },
104
  },
105

  
106
  taxzone => {
107
    class       => 'SL::DB::TaxZone',
108
    key_columns => { taxzone_id => 'id' },
109
  },
110

  
111
  vendor => {
112
    class       => 'SL::DB::Vendor',
113
    key_columns => { vendor_id => 'id' },
114
  },
115
);
116

  
117
1;
118
;
SL/DB/MetaSetup/ReclamationItem.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::ReclamationItem;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('reclamation_items');
10

  
11
__PACKAGE__->meta->columns(
12
  active_discount_source => { type => 'text', default => '', not_null => 1 },
13
  active_price_source    => { type => 'text', default => '', not_null => 1 },
14
  base_qty               => { type => 'float', precision => 4, scale => 4 },
15
  description            => { type => 'text' },
16
  discount               => { type => 'float', precision => 4, scale => 4 },
17
  id                     => { type => 'serial', not_null => 1 },
18
  itime                  => { type => 'timestamp', default => 'now()' },
19
  lastcost               => { type => 'numeric', precision => 15, scale => 5 },
20
  longdescription        => { type => 'text' },
21
  mtime                  => { type => 'timestamp' },
22
  parts_id               => { type => 'integer', not_null => 1 },
23
  position               => { type => 'integer', not_null => 1 },
24
  price_factor           => { type => 'numeric', default => 1, precision => 15, scale => 5 },
25
  price_factor_id        => { type => 'integer' },
26
  pricegroup_id          => { type => 'integer' },
27
  project_id             => { type => 'integer' },
28
  qty                    => { type => 'float', precision => 4, scale => 4 },
29
  reason_description_ext => { type => 'text' },
30
  reason_description_int => { type => 'text' },
31
  reason_id              => { type => 'integer', not_null => 1 },
32
  reclamation_id         => { type => 'integer', not_null => 1 },
33
  reqdate                => { type => 'date' },
34
  sellprice              => { type => 'numeric', precision => 15, scale => 5 },
35
  serialnumber           => { type => 'text' },
36
  unit                   => { type => 'varchar', length => 20 },
37
);
38

  
39
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
40

  
41
__PACKAGE__->meta->allow_inline_column_values(1);
42

  
43
__PACKAGE__->meta->foreign_keys(
44
  part => {
45
    class       => 'SL::DB::Part',
46
    key_columns => { parts_id => 'id' },
47
  },
48

  
49
  price_factor_obj => {
50
    class       => 'SL::DB::PriceFactor',
51
    key_columns => { price_factor_id => 'id' },
52
  },
53

  
54
  pricegroup => {
55
    class       => 'SL::DB::Pricegroup',
56
    key_columns => { pricegroup_id => 'id' },
57
  },
58

  
59
  project => {
60
    class       => 'SL::DB::Project',
61
    key_columns => { project_id => 'id' },
62
  },
63

  
64
  reason => {
65
    class       => 'SL::DB::ReclamationReason',
66
    key_columns => { reason_id => 'id' },
67
  },
68

  
69
  reclamation => {
70
    class       => 'SL::DB::Reclamation',
71
    key_columns => { reclamation_id => 'id' },
72
  },
73

  
74
  unit_obj => {
75
    class       => 'SL::DB::Unit',
76
    key_columns => { unit => 'name' },
77
  },
78
);
79

  
80
1;
81
;
SL/DB/MetaSetup/ReclamationReason.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::ReclamationReason;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('reclamation_reasons');
10

  
11
__PACKAGE__->meta->columns(
12
  description        => { type => 'text', not_null => 1 },
13
  id                 => { type => 'serial', not_null => 1 },
14
  itime              => { type => 'timestamp', default => 'now()' },
15
  mtime              => { type => 'timestamp' },
16
  name               => { type => 'text', not_null => 1 },
17
  position           => { type => 'integer', not_null => 1 },
18
  valid_for_purchase => { type => 'boolean', default => 'false', not_null => 1 },
19
  valid_for_sales    => { type => 'boolean', default => 'false', not_null => 1 },
20
);
21

  
22
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
23

  
24
__PACKAGE__->meta->allow_inline_column_values(1);
25

  
26
1;
27
;
SL/DB/Reclamation.pm
1
package SL::DB::Reclamation;
2

  
3
use utf8;
4
use strict;
5

  
6
use Carp;
7
use DateTime;
8
use List::Util qw(max sum0);
9
use List::MoreUtils qw(any);
10

  
11
use SL::DB::MetaSetup::Reclamation;
12
use SL::DB::Manager::Reclamation;
13
use SL::DB::Helper::Attr;
14
use SL::DB::Helper::AttrHTML;
15
use SL::DB::Helper::AttrSorted;
16
use SL::DB::Helper::FlattenToForm;
17
use SL::DB::Helper::LinkedRecords;
18
use SL::DB::Helper::PriceTaxCalculator;
19
use SL::DB::Helper::PriceUpdater;
20
use SL::DB::Helper::TransNumberGenerator;
21
use SL::Locale::String qw(t8);
22
use SL::RecordLinks;
23
use Rose::DB::Object::Helpers qw(as_tree);
24

  
25
__PACKAGE__->meta->add_relationship(
26

  
27
  reclamation_items => {
28
    type         => 'one to many',
29
    class        => 'SL::DB::ReclamationItem',
30
    column_map   => { id => 'reclamation_id' },
31
    manager_args => {
32
      with_objects => [ 'part', 'reason' ]
33
    }
34
  },
35
  custom_shipto            => {
36
    type                   => 'one to one',
37
    class                  => 'SL::DB::Shipto',
38
    column_map             => { id => 'trans_id' },
39
    query_args             => [ module => 'Reclamation' ],
40
  },
41
  exchangerate_obj         => {
42
    type                   => 'one to one',
43
    class                  => 'SL::DB::Exchangerate',
44
    column_map             => { currency_id => 'currency_id', transdate => 'transdate' },
45
  },
46
);
47

  
48
SL::DB::Helper::Attr::make(__PACKAGE__, daily_exchangerate => 'numeric');
49

  
50
__PACKAGE__->meta->initialize;
51

  
52
__PACKAGE__->attr_html('notes');
53
__PACKAGE__->attr_sorted('items');
54

  
55
__PACKAGE__->before_save('_before_save_set_record_number');
56
__PACKAGE__->before_save('_before_save_remove_empty_custom_shipto');
57
__PACKAGE__->before_save('_before_save_set_custom_shipto_module');
58

  
59
# hooks
60

  
61
sub _before_save_set_record_number {
62
  my ($self) = @_;
63

  
64
  $self->create_trans_number if !$self->record_number;
65

  
66
  return 1;
67
}
68

  
69
sub _before_save_remove_empty_custom_shipto {
70
  my ($self) = @_;
71

  
72
  $self->custom_shipto(undef) if $self->custom_shipto && $self->custom_shipto->is_empty;
73

  
74
  return 1;
75
}
76

  
77
sub _before_save_set_custom_shipto_module {
78
  my ($self) = @_;
79

  
80
  $self->custom_shipto->module('Reclamation') if $self->custom_shipto;
81

  
82
  return 1;
83
}
84

  
85
# methods
86

  
87
sub items { goto &reclamation_items; }
88
sub add_items { goto &add_reclamation_items; }
89
sub record_items { goto &reclamation_items; }
90

  
91
sub type {
92
  my ($self) = @_;
93

  
94
  return 'sales_reclamation'    if $self->customer_id;
95
  return 'purchase_reclamation' if $self->vendor_id;
96

  
97
  return;
98
}
99

  
100
sub is_type {
101
  my ($self, $type) = @_;
102
  return $self->type eq $type;
103
}
104

  
105
sub effective_tax_point {
106
  my ($self) = @_;
107

  
108
  return $self->tax_point || $self->reqdate || $self->transdate;
109
}
110

  
111
sub displayable_type {
112
  my $type = shift->type;
113

  
114
  return $::locale->text('Sales Reclamation')    if $type eq 'sales_reclamation';
115
  return $::locale->text('Purchase Reclamation') if $type eq 'purchase_reclamation';
116

  
117
  die 'invalid type';
118
}
119

  
120
sub displayable_name {
121
  join ' ', grep $_, map $_[0]->$_, qw(displayable_type record_number);
122
};
123

  
124
sub is_sales {
125
  croak 'not an accessor' if @_ > 1;
126
  return !!shift->customer_id;
127
}
128

  
129
sub daily_exchangerate {
130
  my ($self, $val) = @_;
131

  
132
  return 1 if $self->currency_id == $::instance_conf->get_currency_id;
133

  
134
  my $rate = (any { $self->is_type($_) } qw(sales_reclamation))    ? 'buy'
135
           : (any { $self->is_type($_) } qw(purchase_reclamation)) ? 'sell'
136
           : undef;
137
  return if !$rate;
138

  
139
  if (defined $val) {
140
    croak t8('exchange rate has to be positive') if $val <= 0;
141
    if (!$self->exchangerate_obj) {
142
      $self->exchangerate_obj(SL::DB::Exchangerate->new(
143
        currency_id => $self->currency_id,
144
        transdate   => $self->transdate,
145
        $rate       => $val,
146
      ));
147
    } elsif (!defined $self->exchangerate_obj->$rate) {
148
      $self->exchangerate_obj->$rate($val);
149
    } else {
150
      croak t8('exchange rate already exists, no update allowed');
151
    }
152
  }
153
  return $self->exchangerate_obj->$rate if $self->exchangerate_obj;
154
}
155

  
156
sub taxes {
157
  my ($self) = @_;
158
  # add taxes to recalmation
159
  my %pat = $self->calculate_prices_and_taxes();
160
  my @taxes;
161
  foreach my $tax_id (keys %{ $pat{taxes_by_tax_id} }) {
162
    my $netamount = sum0 map { $pat{amounts}->{$_}->{amount} } grep { $pat{amounts}->{$_}->{tax_id} == $tax_id } keys %{ $pat{amounts} };
163
    push(@taxes, { amount    => $pat{taxes_by_tax_id}->{$tax_id},
164
                                netamount => $netamount,
165
                                tax       => SL::DB::Tax->new(id => $tax_id)->load });
166
  }
167
  return \@taxes;
168
}
169

  
170
sub displayable_state {
171
  my ($self) = @_;
172

  
173
  return $self->closed ? $::locale->text('closed') : $::locale->text('open');
174
}
175

  
176
sub valid_reclamation_reasons {
177
  my ($self) = @_;
178

  
179
  my $valid_for_type = ($self->type =~ m{sales} ? 'valid_for_sales' : 'valid_for_purchase');
180
  return SL::DB::Manager::ReclamationReason->get_all_sorted(
181
      where => [  $valid_for_type => 1 ]);
182
}
183

  
184
#TODO(Werner): überprüfen ob alle Felder richtig gestetzt werden
185
sub new_from {
186
  my ($class, $source, %params) = @_;
187
  my %allowed_sources = map { $_ => 1 } qw(
188
    SL::DB::Reclamation
189
  );
190
  unless( $allowed_sources{ref $source} ) {
191
    croak("Unsupported source object type '" . ref($source) . "'");
192
  }
193
  croak("A destination type must be given as parameter") unless $params{destination_type};
194

  
195
  my $destination_type  = delete $params{destination_type};
196

  
197
  my @from_tos = (
198
    #Reclamation
199
    { from => 'sales_reclamation',       to => 'sales_reclamation',    abbr => 'srsr', },
200
    { from => 'purchase_reclamation',    to => 'purchase_reclamation', abbr => 'prpr', },
201
    { from => 'sales_reclamation',       to => 'purchase_reclamation', abbr => 'srpr', },
202
    { from => 'purchase_reclamation',    to => 'sales_reclamation',    abbr => 'prsr', },
203
  );
204
  my $from_to = (grep { $_->{from} eq $source->type && $_->{to} eq $destination_type} @from_tos)[0];
205
  if (!$from_to) {
206
    croak("Cannot convert from '" . $source->type . "' to '" . $destination_type . "'");
207
  }
208

  
209
  my $is_abbr_any = sub {
210
    any { $from_to->{abbr} eq $_ } @_;
211
  };
212

  
213
  my %record_args = (
214
    record_number => undef,
215
    employee => SL::DB::Manager::Employee->current,
216
    closed    => 0,
217
    delivered => 0,
218
    transdate => DateTime->today_local,
219
    reqdate => DateTime->today_local->next_workday(),
220
  );
221
  if ( $is_abbr_any->(qw(srsr prpr srpr prsr)) ) { #Reclamation
222
    map { $record_args{$_} = $source->$_ } # {{{ for vim folds
223
    qw(
224
      amount
225
      contact_id
226
      currency_id
227
      customer_id
228
      cv_record_number
229
      delivery_term_id
230
      department_id
231
      exchangerate
232
      globalproject_id
233
      intnotes
234
      language_id
235
      netamount
236
      notes
237
      payment_id
238
      salesman_id
239
      shippingpoint
240
      shipvia
241
      tax_point
242
      taxincluded
243
      taxzone_id
244
      transaction_description
245
      vendor_id
246
    ); # }}} for vim folds
247
  }
248

  
249
  if ( ($from_to->{from} =~ m{sales}) && ($from_to->{to} =~ m{purchase}) ) {
250
    $record_args{customer_id}      = undef;
251
    $record_args{salesman_id}      = undef;
252
    $record_args{payment_id}       = undef;
253
    $record_args{delivery_term_id} = undef;
254
  }
255
  if ( ($from_to->{from} =~ m{purchase}) && ($from_to->{to} =~ m{sales}) ) {
256
    $record_args{vendor_id} = undef;
257
    $record_args{salesman_id} = undef;
258
    $record_args{payment_id} = undef;
259
  }
260

  
261

  
262
  if ($source->can('shipto_id')) {
263
    # Custom shipto addresses (the ones specific to the sales/purchase record and
264
    # not to the customer/vendor) are only linked from shipto → record.
265
    # Meaning record.shipto_id will not be filled in that case.
266
    if (!$source->shipto_id && $source->id) {
267
      $record_args{custom_shipto} = $source->custom_shipto->clone($class) if $source->can('custom_shipto') && $source->custom_shipto;
268
    } elsif ($source->shipto_id) {
269
      $record_args{shipto_id} = $source->shipto_id;
270
    }
271
  }
272

  
273
  my $reclamation = $class->new(%record_args);
274
  $reclamation->assign_attributes(%{ $params{attributes} }) if $params{attributes};
275

  
276
  unless ($params{no_linked_records}) {
277
    $reclamation->{"converted_from_record_type_ref"} = ref($source);
278
    $reclamation->{"converted_from_record_id"} = $source->id;
279
  };
280

  
281
  my $items = delete($params{items}) || $source->items;
282

  
283
  my @items = map { SL::DB::ReclamationItem->new_from($_, $from_to->{to}, no_linked_records => $params{no_linked_records}); } @{ $items };
284

  
285
  @items = grep { $params{item_filter}->($_) } @items if $params{item_filter};
286
  @items = grep { $_->qty * 1 } @items if $params{skip_items_zero_qty};
287
  @items = grep { $_->qty >=0 } @items if $params{skip_items_negative_qty};
288

  
289
  $reclamation->items(\@items);
290
  return $reclamation;
291
}
292

  
293
sub customervendor {
294
  my ($reclamation) = @_;
295
  return $reclamation->is_sales ? $reclamation->customer : $reclamation->vendor;
296
}
297

  
298
sub date {
299
  goto &transdate;
300
}
301

  
302
sub digest {
303
  my ($self) = @_;
304

  
305
  sprintf "%s %s %s (%s)",
306
    $self->record_number,
307
    $self->customervendor->name,
308
    $self->amount_as_number,
309
    $self->date->to_kivitendo;
310
}
311

  
312
1;
313

  
314
__END__
315

  
316
=pod
317

  
318
=encoding utf8
319

  
320
=head1 NAME
321

  
322
SL::DB::Reclamation - reclamation Datenbank Objekt.
323

  
324
=head1 FUNCTIONS
325

  
326
=head2 C<type>
327

  
328
Returns one of the following string types:
329

  
330
=over 4
331

  
332
=item sales_reclamation
333

  
334
=item purchase_reclamation
335

  
336
=item sales_quotation
337

  
338
=item request_quotation
339

  
340
=back
341

  
342
=head2 C<is_type TYPE>
343

  
344
Returns true if the reclamation is of the given type.
345

  
346
=head2 C<daily_exchangerate $val>
347

  
348
Gets or sets the exchangerate object's value. This is the value from the
349
table C<exchangerate> depending on the reclamation's currency, the transdate and
350
if it is a sales or purchase reclamation.
351

  
352
The reclamation object (respectively the table C<oe>) has an own column
353
C<exchangerate> which can be get or set with the accessor C<exchangerate>.
354

  
355
The idea is to drop the legacy table C<exchangerate> in the future and to
356
give all relevant tables it's own C<exchangerate> column.
357

  
358
So, this method is here if you need to access the "legacy" exchangerate via
359
an reclamation object.
360

  
361
=over 4
362

  
363
=item C<$val>
364

  
365
(optional) If given, the exchangerate in the "legacy" table is set to this
366
value, depending on currency, transdate and sales or purchase.
367

  
368
=back
369

  
370
=head2 C<convert_to_delivery_order %params>
371

  
372
Creates a new delivery reclamation with C<$self> as the basis by calling
373
L<SL::DB::DeliveryReclamation::new_from>. That delivery reclamation is saved, and
374
C<$self> is linked to the new invoice via
375
L<SL::DB::RecordLink>. C<$self>'s C<delivered> attribute is set to
376
C<true>, and C<$self> is saved.
377

  
378
The arguments in C<%params> are passed to
379
L<SL::DB::DeliveryReclamation::new_from>.
380

  
381
Returns C<undef> on failure. Otherwise the new delivery reclamation will be
382
returned.
383

  
384
=head2 C<convert_to_invoice %params>
385

  
386
Creates a new invoice with C<$self> as the basis by calling
387
L<SL::DB::Invoice::new_from>. That invoice is posted, and C<$self> is
388
linked to the new invoice via L<SL::DB::RecordLink>. C<$self>'s
389
C<closed> attribute is set to C<true>, and C<$self> is saved.
390

  
391
The arguments in C<%params> are passed to L<SL::DB::Invoice::post>.
392

  
393
Returns the new invoice instance on success and C<undef> on
394
failure. The whole process is run inside a transaction. On failure
395
nothing is created or changed in the database.
396

  
397
At the moment only sales quotations and sales reclamations can be converted.
398

  
399
=head2 C<new_from $source, %params>
400

  
401
Creates a new C<SL::DB::Reclamation> instance and copies as much
402
information from C<$source> as possible. At the moment only records with the
403
same destination type as the source type and sales reclamations from
404
sales quotations and purchase reclamations from requests for quotations can be
405
created.
406

  
407
The C<transdate> field will be set to the current date.
408

  
409
The conversion copies the reclamation items as well.
410

  
411
Returns the new reclamation instance. The object returned is not
412
saved.
413

  
414
C<%params> can include the following options
415
(C<destination_type> is mandatory):
416

  
417
=over 4
418

  
419
=item C<destination_type>
420

  
421
(mandatory)
422
The type of the newly created object. Can be C<sales_quotation>,
423
C<sales_reclamation>, C<purchase_quotation> or C<purchase_reclamation> for now.
424

  
425
=item C<items>
426

  
427
An optional array reference of RDBO instances for the items to use. If
428
missing then the method C<items_sorted> will be called on
429
C<$source>. This option can be used to override the sorting, to
430
exclude certain positions or to add additional ones.
431

  
432
=item C<skip_items_negative_qty>
433

  
434
If trueish then items with a negative quantity are skipped. Items with
435
a quantity of 0 are not affected by this option.
436

  
437
=item C<skip_items_zero_qty>
438

  
439
If trueish then items with a quantity of 0 are skipped.
440

  
441
=item C<item_filter>
442

  
443
An optional code reference that is called for each item with the item
444
as its sole parameter. Items for which the code reference returns a
445
falsish value will be skipped.
446

  
447
=item C<attributes>
448

  
449
An optional hash reference. If it exists then it is passed to C<new>
450
allowing the caller to set certain attributes for the new delivery
451
reclamation.
452

  
453
=back
454

  
455
=head1 BUGS
456

  
457
Nothing here yet.
458

  
459
=head1 AUTHOR
460

  
461
Sven Schöling <s.schoeling@linet-services.de>
462

  
463
=cut
SL/DB/ReclamationItem.pm
1
package SL::DB::ReclamationItem;
2

  
3
use utf8;
4
use strict;
5

  
6
use List::MoreUtils qw(any);
7

  
8
use SL::DB::MetaSetup::ReclamationItem;
9
use SL::DB::Manager::ReclamationItem;
10
use SL::DB::ReclamationReason;
11
use SL::DB::Helper::ActsAsList;
12
use SL::DB::Helper::LinkedRecords;
13
use SL::DB::Helper::RecordItem;
14
use SL::DB::Helper::CustomVariables (
15
  sub_module  => 'reclamation_items',
16
  cvars_alias => 1,
17
  overloads   => {
18
    parts_id => {
19
      class => 'SL::DB::Part',
20
      module => 'IC',
21
    }
22
  },
23
);
24
use SL::Helper::ShippedQty;
25

  
26
__PACKAGE__->meta->initialize;
27

  
28
__PACKAGE__->configure_acts_as_list(group_by => [qw(reclamation_id)]);
29

  
30
sub shipped_qty {
31
  my ($self, %params) = @_;
32

  
33
  my $force = delete $params{force};
34

  
35
  SL::Helper::ShippedQty->new(%params)->calculate($self)->write_to_objects if $force || !defined $self->{shipped_qty};
36

  
37
  $self->{shipped_qty};
38
}
39

  
40
sub is_linked_to_record {
41
  my ($self) = @_;
42

  
43
  if(scalar(@{$self->linked_records}) || $self->{converted_from_record_item_type_ref}) {
44
    return 1;
45
  }
46

  
47
  return 0;
48
}
49

  
50
#TODO(Werner): überprüfen ob alle Felder richtig gestetzt werden
51
sub new_from {
52
  my ($class, $source, $parent_type, %params) = @_;
53
  unless (any {ref($source) eq $_}
54
    qw(
55
      SL::DB::ReclamationItem
56
    )
57
  ) {
58
    croak("Unsupported source object type '" . ref($source) . "'");
59
  }
60
  my @custom_variables = map { _clone_cvar_for_reclamation_item($_) } @{ $source->custom_variables };
61

  
62

  
63
  my %item_args;
64
  if (ref($source) eq 'SL::DB::ReclamationItem') {
65
    map { $item_args{$_} = $source->$_ } qw(
66
      active_discount_source active_price_source base_qty description discount
67
      lastcost longdescription parts_id position price_factor price_factor_id
68
      pricegroup_id project_id qty reason_description_ext reason_description_int
69
      reason_id reqdate sellprice serialnumber
70
      unit
71
    );
72
    $item_args{custom_variables} = \@custom_variables;
73
  }
74

  
75
  my $item = $class->new(%item_args);
76

  
77
  if ( $source->record->is_sales() && ($parent_type =~ m{sales}) ) {
78
    $item->sellprice($source->lastcost);
79
    $item->discount(0);
80
  }
81
  if ( !$source->record->is_sales() && ($parent_type =~ m{purchase}) ) {
82
    $item->lastcost($source->sellprice);
83
  }
84

  
85
  $item->assign_attributes(%{ $params{attributes} }) if $params{attributes};
86

  
87
  unless ($params{no_linked_records}) {
88
    $item->{"converted_from_record_item_type_ref"} = ref($source);
89
    $item->{"converted_from_record_item_id"} = $source->id;
90
  }
91

  
92
  return $item;
93
}
94

  
95
sub _clone_cvar_for_reclamation_item {
96
  my ($cvar) = @_;
97

  
98
  my $cloned = $_->clone_and_reset;
99
  $cloned->sub_module('reclamation_items');
100

  
101
  return $cloned;
102
}
103

  
104
sub customervendor {
105
  my ($self) = @_;
106

  
107
  return $self->reclamation->customervendor;
108
}
109

  
110
sub delivered_qty { goto &shipped_qty }
111
sub record { goto &reclamation }
112
sub record_id { goto &reclamation_id }
113
sub trans_id { goto &reclamation_id }
114
sub date { goto &reqdate }
115

  
116
1;
117

  
118
__END__
119

  
120
=pod
121

  
122
=head1 NAME
123

  
124
SL::DB::ReclamationItems: Rose model for reclamationitems
125

  
126
=head1 FUNCTIONS
127

  
128
=over 4
129

  
130
=item C<shipped_qty PARAMS>
131

  
132
Calculates the shipped qty for this reclamationitem (measured in the current unit)
133
and returns it.
134

  
135
Note that the shipped qty is expected not to change within the request and is
136
cached in C<shipped_qty> once calculated. If C<< force => 1 >> is passed, the
137
existibng cache is ignored.
138

  
139
Given parameters will be passed to L<SL::Helper::ShippedQty>, so you can force
140
the shipped/delivered distinction like this:
141

  
142
  $_->shipped_qty(require_stock_out => 0);
143

  
144
Note however that calculating shipped_qty on individual Reclamationitems is generally
145
a bad idea. See L<SL::Helper::ShippedQty> for way to compute these all at once.
146

  
147
=item C<delivered_qty>
148

  
149
Alias for L</shipped_qty>.
150

  
151
=back
152

  
153
=head1 AUTHORS
154

  
155
G. Richardson E<lt>grichardson@kivitendo-premium.deE<gt>
156

  
157
=cut
SL/DB/ReclamationReason.pm
1
package SL::DB::ReclamationReason;
2

  
3
use strict;
4

  
5
use SL::DB::MetaSetup::ReclamationReason;
6
use SL::DB::Manager::ReclamationReason;
7
use SL::DB::Helper::ActsAsList
8

  
9
__PACKAGE__->meta->initialize;
10

  
11
sub validate {
12
  my ($self) = @_;
13

  
14
  my @errors;
15
  if (!$self->name) {
16
      push @errors, $::locale->text('The name is missing.');
17
  }
18
  if (!$self->description) {
19
    push @errors, $::locale->text('The description is missing.');
20
  }
21
  return @errors;
22
}
23

  
24
1;
scripts/rose_auto_create_model.pl
72 72
    ap                        => { payment_id => 'payment_terms', },
73 73

  
74 74
    orderitems                => { parts_id => 'part', trans_id => 'order', },
75
    reclamation_items         => { parts_id => 'part' },
75 76
    delivery_order_items      => { parts_id => 'part' },
76 77
    invoice                   => { parts_id => 'part' },
77 78
    follow_ups                => { created_for_user => 'created_for_employee', created_by => 'created_by_employee', },
sql/Pg-upgrade2/clean_up_record_links_before_delete_trigger.sql
1
-- @tag: clean_up_record_links_before_delete_trigger
2
-- @description: delete trigger for record_links clean up
3
-- @depends: release_3_5_7
4

  
5
CREATE OR REPLACE FUNCTION clean_up_record_links_before_delete() RETURNS trigger AS $$
6
  BEGIN
7
    DELETE FROM record_links
8
      WHERE (from_table = TG_TABLE_NAME AND from_id = OLD.id)
9
         OR (to_table   = TG_TABLE_NAME AND to_id   = OLD.id);
10
    RETURN OLD;
11
  END;
12
$$ LANGUAGE plpgsql;
sql/Pg-upgrade2/delete_cvars_on_trans_deletion_add_reclamation_items.sql
1
-- @tag: delete_cvars_on_trans_deletion_add_reclamation_items
2
-- @description: Add reclamation_items to trigger
3
-- @depends: delete_cvars_on_trans_deletion_add_shipto reclamations
4

  
5
CREATE OR REPLACE FUNCTION delete_custom_variables_trigger()
6
RETURNS TRIGGER AS $$
7
  BEGIN
8
    IF (TG_TABLE_NAME IN ('orderitems', 'delivery_order_items', 'invoice', 'reclamation_items')) THEN
9
      PERFORM delete_custom_variables_with_sub_module('IC', TG_TABLE_NAME, old.id);
10
    END IF;
11

  
12
    IF (TG_TABLE_NAME = 'parts') THEN
13
      PERFORM delete_custom_variables_with_sub_module('IC', '', old.id);
14
    END IF;
15

  
16
    IF (TG_TABLE_NAME IN ('customer', 'vendor')) THEN
17
      PERFORM delete_custom_variables_with_sub_module('CT', '', old.id);
18
    END IF;
19

  
20
    IF (TG_TABLE_NAME = 'contacts') THEN
21
      PERFORM delete_custom_variables_with_sub_module('Contacts', '', old.cp_id);
22
    END IF;
23

  
24
    IF (TG_TABLE_NAME = 'project') THEN
25
      PERFORM delete_custom_variables_with_sub_module('Projects', '', old.id);
26
    END IF;
27

  
28
    IF (TG_TABLE_NAME = 'shipto') THEN
29
      PERFORM delete_custom_variables_with_sub_module('ShipTo', '', old.shipto_id);
30
    END IF;
31

  
32
    RETURN old;
33
  END;
34
$$ LANGUAGE plpgsql;
35

  
36
CREATE TRIGGER reclamation_items_delete_custom_variables_after_deletion
37
AFTER DELETE ON reclamation_items
38
FOR EACH ROW EXECUTE PROCEDURE delete_custom_variables_trigger();
sql/Pg-upgrade2/reclamations.sql
1
-- @tag: reclamations
2
-- @description: Add reclamations, reclamation_items and reclamation_reasons
3
-- @depends: release_3_5_7
4
-- @ignore: 0
5

  
6
CREATE TABLE reclamation_reasons (
7
  id                       SERIAL                      PRIMARY KEY,
8
  name                     TEXT                        NOT NULL,
9
  description              TEXT                        NOT NULL,
10
  position                 INTEGER                     NOT NULL,
11
  itime                    TIMESTAMP without time zone DEFAULT now(),
12
  mtime                    TIMESTAMP without time zone,
13
  valid_for_sales          BOOLEAN                     NOT NULL DEFAULT false,
14
  valid_for_purchase       BOOLEAN                     NOT NULL DEFAULT false
15
);
16
CREATE TRIGGER mtime_reclamation_reasons
17
  BEFORE UPDATE ON reclamation_reasons
18
  FOR EACH ROW EXECUTE PROCEDURE set_mtime();
19

  
20
CREATE TABLE reclamations (
21
--basic
22
  id                      INTEGER                     NOT NULL DEFAULT nextval('id'),
23
  record_number           TEXT                        NOT NULL,
24
  transdate               DATE                        DEFAULT now(),
25
  itime                   TIMESTAMP without time zone DEFAULT now(),
26
  mtime                   TIMESTAMP without time zone,
27
  delivered               BOOLEAN                     NOT NULL DEFAULT false,
28
  closed                  BOOLEAN                     NOT NULL DEFAULT false,
29
--header
30
  employee_id             INTEGER                     NOT NULL REFERENCES employee(id),
31
  globalproject_id        INTEGER                     REFERENCES project(id),
32
  delivery_term_id        INTEGER                     REFERENCES delivery_terms(id),
33
  shipto_id               INTEGER                     REFERENCES shipto(shipto_id),
34
  department_id           INTEGER                     REFERENCES department(id),
35
  contact_id              INTEGER                     REFERENCES contacts(cp_id),
36
  shipvia                 TEXT,
37
  transaction_description TEXT,
38
  shippingpoint           TEXT,
39
  cv_record_number        TEXT,
40
  reqdate                 DATE,
41
--money/summery
42
  amount                  NUMERIC(15,5),
43
  netamount               NUMERIC(15,5),
44
  payment_id              INTEGER                     REFERENCES payment_terms(id),
45
  currency_id             INTEGER                     NOT NULL REFERENCES currencies(id),
46
  taxincluded             BOOLEAN                     NOT NULL,
47
  tax_point               DATE,
48
  exchangerate            NUMERIC(15,5),
49
  taxzone_id              INTEGER                     NOT NULL REFERENCES tax_zones(id),
50
--other
51
  notes                   TEXT,
52
  intnotes                TEXT,
53
  language_id             INTEGER                     REFERENCES language(id),
54

  
55
  salesman_id             INTEGER                     REFERENCES employee(id),
56
  customer_id             INTEGER                     REFERENCES customer(id),
57

  
58
  vendor_id               INTEGER                     REFERENCES vendor(id),
59

  
60
  CONSTRAINT reclamations_customervendor_check CHECK (
61
       (customer_id IS NOT NULL AND vendor_id   IS NULL)
62
    OR (vendor_id   IS NOT NULL AND customer_id IS NULL)
63
  ),
64

  
65
  PRIMARY KEY (id)
66
);
67
CREATE TRIGGER mtime_reclamations BEFORE UPDATE ON reclamations FOR EACH ROW EXECUTE PROCEDURE set_mtime();
68

  
69
ALTER TABLE defaults ADD COLUMN p_reclamation_record_number TEXT NOT NULL DEFAULT 0;
70
ALTER TABLE defaults ADD COLUMN s_reclamation_record_number TEXT NOT NULL DEFAULT 0;
71

  
72
CREATE TABLE reclamation_items (
73
--base
74
  id                         SERIAL                      PRIMARY KEY,
75
  reclamation_id             INTEGER                     NOT NULL REFERENCES reclamations(id) ON DELETE CASCADE,
76
  reason_id                  INTEGER                     NOT NULL REFERENCES reclamation_reasons(id),
77
  reason_description_ext     TEXT,
78
  reason_description_int     TEXT,
79
  position                   INTEGER                     NOT NULL CHECK(position > 0),
80
  itime                      TIMESTAMP without time zone DEFAULT now(),
81
  mtime                      TIMESTAMP without time zone,
82
--header
83
  project_id                 INTEGER                     REFERENCES project(id) ON DELETE SET NULL,
84
--part description
85
  parts_id                   INTEGER                     NOT NULL REFERENCES parts(id),
86
  description                TEXT,
87
  longdescription            TEXT,
88
  serialnumber               TEXT,
89
  base_qty                   REAL,
90
  qty                        REAL,
91
  unit                       character varying(20)       REFERENCES units(name),
92
--money
93
  sellprice                  NUMERIC(15,5),
94
  lastcost                   NUMERIC(15,5),
95
  discount                   REAL,
96
  pricegroup_id              INTEGER                     REFERENCES pricegroup(id),
97
  price_factor_id            INTEGER                     REFERENCES price_factors(id),
98
  price_factor               NUMERIC(15,5)               DEFAULT 1,
99
  active_price_source        TEXT                        NOT NULL DEFAULT ''::text,
100
  active_discount_source     TEXT                        NOT NULL DEFAULT ''::text,
101
--other
102
  reqdate                    DATE
103
);
104
CREATE TRIGGER mtime_reclamation_items BEFORE UPDATE ON reclamation_items FOR EACH ROW EXECUTE PROCEDURE set_mtime();
sql/Pg-upgrade2/record_links_delete_triggers__reclamations__reclamation_items.sql
1
-- @tag: record_links_delete_triggers__reclamations__reclamation_items
2
-- @description: delete corresponding record_links if reclamation or reclamation_item is deleted
3
-- @depends: clean_up_record_links_before_delete_trigger reclamations
4

  
5
CREATE TRIGGER before_delete_reclamation_items_clean_up_record_linkes_trigger
6
BEFORE DELETE ON reclamation_items FOR EACH ROW EXECUTE
7
PROCEDURE clean_up_record_links_before_delete();
8

  
9
CREATE TRIGGER before_delete_reclamations_clean_up_record_linkes_trigger
10
BEFORE DELETE ON reclamations FOR EACH ROW EXECUTE
11
PROCEDURE clean_up_record_links_before_delete();

Auch abrufbar als: Unified diff