Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision ac552280

Von Moritz Bunkus vor fast 14 Jahren hinzugefügt

  • ID ac5522802741d3092b371ab6b57199cb8a587bf9
  • Vorgänger f174fe7f
  • Nachfolger 624c53dc

Verzeichnis SL/DB/Helpers in SL/DB/Helper umbenannt (Konsistenz)

Unterschiede anzeigen:

SL/DB/Helper/ALL.pm
1
package SL::DB::Helper::ALL;
2

  
3
use strict;
4

  
5
use SL::DB::AccTrans;
6
use SL::DB::AccTransaction;
7
use SL::DB::Assembly;
8
use SL::DB::AuditTrail;
9
use SL::DB::BankAccount;
10
use SL::DB::Bin;
11
use SL::DB::Buchungsgruppe;
12
use SL::DB::Business;
13
use SL::DB::Chart;
14
use SL::DB::Contact;
15
use SL::DB::CustomVariable;
16
use SL::DB::CustomVariableConfig;
17
use SL::DB::CustomVariableValidity;
18
use SL::DB::Customer;
19
use SL::DB::CustomerTax;
20
use SL::DB::Datev;
21
use SL::DB::Default;
22
use SL::DB::DeliveryOrder;
23
use SL::DB::DeliveryOrderItem;
24
use SL::DB::DeliveryOrderItemsStock;
25
use SL::DB::Department;
26
use SL::DB::DptTrans;
27
use SL::DB::Draft;
28
use SL::DB::Dunning;
29
use SL::DB::DunningConfig;
30
use SL::DB::Employee;
31
use SL::DB::Exchangerate;
32
use SL::DB::Finanzamt;
33
use SL::DB::FollowUp;
34
use SL::DB::FollowUpAccess;
35
use SL::DB::FollowUpLink;
36
use SL::DB::GLTransaction;
37
use SL::DB::GenericTranslation;
38
use SL::DB::Gifi;
39
use SL::DB::History;
40
use SL::DB::Inventory;
41
use SL::DB::Invoice;
42
use SL::DB::InvoiceItem;
43
use SL::DB::Language;
44
use SL::DB::License;
45
use SL::DB::LicenseInvoice;
46
use SL::DB::MakeModel;
47
use SL::DB::Note;
48
use SL::DB::Order;
49
use SL::DB::OrderItem;
50
use SL::DB::Part;
51
use SL::DB::PartsGroup;
52
use SL::DB::PartsTax;
53
use SL::DB::PaymentTerm;
54
use SL::DB::PriceFactor;
55
use SL::DB::Pricegroup;
56
use SL::DB::Prices;
57
use SL::DB::Printer;
58
use SL::DB::Project;
59
use SL::DB::PurchaseInvoice;
60
use SL::DB::RMA;
61
use SL::DB::RMAItem;
62
use SL::DB::RecordLink;
63
use SL::DB::SchemaInfo;
64
use SL::DB::SepaExport;
65
use SL::DB::SepaExportItem;
66
use SL::DB::Shipto;
67
use SL::DB::Status;
68
use SL::DB::Tax;
69
use SL::DB::TaxKey;
70
use SL::DB::TaxZone;
71
use SL::DB::TodoUserConfig;
72
use SL::DB::TransferType;
73
use SL::DB::Translation;
74
use SL::DB::TranslationPaymentTerm;
75
use SL::DB::Unit;
76
use SL::DB::UnitsLanguage;
77
use SL::DB::Vendor;
78
use SL::DB::VendorTax;
79
use SL::DB::Warehouse;
80

  
81
1;
82

  
83
__END__
84

  
85
=pod
86

  
87
=head1 NAME
88

  
89
SL::DB::Helper::ALL: Dependency-only package for all SL::DB::* modules
90

  
91
=head1 SYNOPSIS
92

  
93
  use SL::DB::Helper::ALL;
94

  
95
=head1 DESCRIPTION
96

  
97
This module depends on all modules in SL/DB/*.pm for the convenience
98
of being able to write a simple \C<use SL::DB::Helper::ALL> and
99
having everything loaded. This is supposed to be used only in the
100
Lx-Office console. Normal modules should C<use> only the modules they
101
actually need.
102

  
103
=head1 AUTHOR
104

  
105
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
106

  
107
=cut
SL/DB/Helper/Attr.pm
1
package SL::DB::Helper::Attr;
2

  
3
use strict;
4

  
5
sub auto_make {
6
  my ($package, %params) = @_;
7

  
8
  for my $col ($package->meta->columns) {
9
    next if $col->primary_key_position; # don't make attr helper for primary keys
10
    _make_by_type($package, $col->name, $col->type);
11
  }
12

  
13
  return $package;
14
}
15

  
16
sub make {
17
  my ($package, %params) = @_;
18

  
19
  for my $name (keys %params) {
20
    my @types = ref $params{$name} eq 'ARRAY' ? @{ $params{$name} } : ($params{$name});
21
    for my $type (@types) {
22
      _make_by_type($package, $name, $type);
23
    }
24
  }
25
  return $package;
26
}
27

  
28

  
29

  
30
sub _make_by_type {
31
  my ($package, $name, $type) = @_;
32
  _as_number ($package, $name, places => -2) if $type =~ /numeric | real | float/xi;
33
  _as_percent($package, $name, places =>  0) if $type =~ /numeric | real | float/xi;
34
  _as_number ($package, $name, places =>  0) if $type =~ /int/xi;
35
  _as_date   ($package, $name)               if $type =~ /date | timestamp/xi;
36
}
37

  
38
sub _as_number {
39
  my $package     = shift;
40
  my $attribute   = shift;
41
  my %params      = @_;
42

  
43
  $params{places} = 2 if !defined($params{places});
44

  
45
  no strict 'refs';
46
  *{ $package . '::' . $attribute . '_as_number' } = sub {
47
    my ($self, $string) = @_;
48

  
49
    $self->$attribute($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
50

  
51
    return $::form->format_amount(\%::myconfig, $self->$attribute, $params{places});
52
  };
53
}
54

  
55
sub _as_percent {
56
  my $package     = shift;
57
  my $attribute   = shift;
58
  my %params      = @_;
59

  
60
  $params{places} = 2 if !defined($params{places});
61

  
62
  no strict 'refs';
63
  *{ $package . '::' . $attribute . '_as_percent' } = sub {
64
    my ($self, $string) = @_;
65

  
66
    $self->$attribute($::form->parse_amount(\%::myconfig, $string) / 100) if @_ > 1;
67

  
68
    return $::form->format_amount(\%::myconfig, 100 * $self->$attribute, $params{places});
69
  };
70

  
71
  return 1;
72
}
73

  
74
sub _as_date {
75
  my $package     = shift;
76
  my $attribute   = shift;
77
  my %params      = @_;
78

  
79
  no strict 'refs';
80
  *{ $package . '::' . $attribute . '_as_date' } = sub {
81
    my ($self, $string) = @_;
82

  
83
    if (@_ > 1) {
84
      if ($string) {
85
        my ($yy, $mm, $dd) = $::locale->parse_date(\%::myconfig, $string);
86
        $self->$attribute(DateTime->new(year => $yy, month => $mm, day => $dd));
87
      } else {
88
        $self->$attribute(undef);
89
      }
90
    }
91

  
92
    return $self->$attribute
93
      ? $::locale->reformat_date(
94
          { dateformat => 'yy-mm-dd' },
95
          ( $self->$attribute eq 'now'
96
             ? DateTime->now
97
             : $self->$attribute
98
          )->ymd,
99
          $::myconfig{dateformat}
100
        )
101
      : undef;
102
  };
103

  
104
  return 1;
105
}
106

  
107
1;
108

  
109

  
110
1;
111

  
112
__END__
113

  
114
=head1 NAME
115

  
116
SL::DB::Helper::Attr - attribute helpers
117

  
118
=head1 SYNOPSIS
119

  
120
  use SL::DB::Helper::Attr;
121
  SL::DB::Helper::Attr::make($class,
122
    method_name => 'numeric(15,5)',
123
    datemethod  => 'date'
124
  );
125
  SL::DB::Helper::Attr::auto_make($class);
126

  
127
=head1 DESCRIPTION
128

  
129
=head1 FUNCTIONS
130

  
131
=head1 BUGS
132

  
133
=head1 AUTHOR
134

  
135
=cut
SL/DB/Helper/ConventionManager.pm
1
package SL::DB::Helper::ConventionManager;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::ConventionManager;
6

  
7
use base qw(Rose::DB::Object::ConventionManager);
8

  
9
sub auto_manager_class_name {
10
  my $self         = shift;
11
  my $object_class = shift || $self->meta->class;
12

  
13
  my @parts        = split m/::/, $object_class;
14
  my $last         = pop @parts;
15

  
16
  return join('::', @parts, 'Manager', $last);
17
}
18

  
19
# Base name used for 'make_manager_class', e.g. 'get_all',
20
# 'update_all'
21
sub auto_manager_base_name {
22
  return 'all';
23
}
24

  
25
sub auto_manager_base_class {
26
  return 'SL::DB::Helper::Manager';
27
}
28

  
29
1;
SL/DB/Helper/Manager.pm
1
package SL::DB::Helper::Manager;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::Manager;
6
use base qw(Rose::DB::Object::Manager);
7

  
8
sub make_manager_methods {
9
  my $class  = shift;
10
  my @params = scalar(@_) ? @_ : qw(all);
11
  return $class->SUPER::make_manager_methods(@params);
12
}
13

  
14
sub find_by {
15
  my $class = shift;
16

  
17
  return if !@_;
18
  return $class->get_all(query => [ @_ ], limit => 1)->[0];
19
}
20

  
21
sub get_first {
22
  shift->get_all(
23
    limit => 1,
24
  )->[0];
25
}
26

  
27
1;
SL/DB/Helper/Mappings.pm
1
package SL::DB::Helpers::Mappings;
2

  
3
use utf8;
4
use strict;
5

  
6
# these will not be managed as Rose::DB models, because they are not normalized,
7
# significant changes are needed to get them done, or they were done by CRM.
8
my @lxoffice_blacklist_permanent = qw(
9
  leads
10
);
11

  
12
# these are not managed _yet_, but will hopefully at some point.
13
# if you are confident that one of these works, remove it here.
14
my @lxoffice_blacklist_temp = qw(
15
);
16

  
17
my @lxoffice_blacklist = (@lxoffice_blacklist_permanent, @lxoffice_blacklist_temp);
18

  
19
# map table names to their models.
20
# unlike rails we have no singular<->plural magic.
21
# remeber: tables should be named as the plural of the model name.
22
my %lxoffice_package_names = (
23
  acc_trans                      => 'acc_transaction',
24
  audittrail                     => 'audit_trail',
25
  ar                             => 'invoice',
26
  ap                             => 'purchase_invoice',
27
  bank_accounts                  => 'bank_account',
28
  buchungsgruppen                => 'buchungsgruppe',
29
  contacts                       => 'contact',
30
  custom_variable_configs        => 'custom_variable_config',
31
  custom_variables               => 'custom_variable',
32
  custom_variables_validity      => 'custom_variable_validity',
33
  customertax                    => 'customer_tax',
34
  datev                          => 'datev',
35
  defaults                       => 'default',
36
  delivery_orders                => 'delivery_order',
37
  delivery_order_items           => 'delivery_order_item',
38
  department                     => 'department',
39
  dpt_trans                      => 'dpt_trans',
40
  drafts                         => 'draft',
41
  dunning                        => 'dunning',
42
  dunning_config                 => 'dunning_config',
43
  employee                       => 'employee',
44
  exchangerate                   => 'exchangerate',
45
  finanzamt                      => 'finanzamt',
46
  follow_up_access               => 'follow_up_access',
47
  follow_up_links                => 'follow_up_link',
48
  follow_ups                     => 'follow_up',
49
  generic_translations           => 'generic_translation',
50
  gifi                           => 'gifi',
51
  gl                             => 'GLTransaction',
52
  history_erp                    => 'history',
53
  inventory                      => 'inventory',
54
  invoice                        => 'invoice_item',
55
  language                       => 'language',
56
  license                        => 'license',
57
  licenseinvoice                 => 'license_invoice',
58
  makemodel                      => 'make_model',
59
  notes                          => 'note',
60
  orderitems                     => 'order_item',
61
  oe                             => 'order',
62
  parts                          => 'part',
63
  partsgroup                     => 'parts_group',
64
  partstax                       => 'parts_tax',
65
  payment_terms                  => 'payment_term',
66
  prices                         => 'prices',
67
  price_factors                  => 'price_factor',
68
  pricegroup                     => 'pricegroup',
69
  printers                       => 'Printer',
70
  record_links                   => 'record_link',
71
  rma                            => 'RMA',
72
  rmaitems                       => 'RMA_item',
73
  sepa_export                    => 'sepa_export',
74
  sepa_export_items              => 'sepa_export_item',
75
  schema_info                    => 'schema_info',
76
  status                         => 'status',
77
  tax                            => 'tax',
78
  taxkeys                        => 'tax_key',
79
  tax_zones                      => 'tax_zone',
80
  todo_user_config               => 'todo_user_config',
81
  translation                    => 'translation',
82
  translation_payment_terms      => 'translation_payment_term',
83
  units                          => 'unit',
84
  units_language                 => 'units_language',
85
  vendortax                      => 'vendor_tax',
86
);
87

  
88
sub get_blacklist {
89
  return LXOFFICE => \@lxoffice_blacklist;
90
}
91

  
92
sub get_package_names {
93
  return LXOFFICE => \%lxoffice_package_names;
94
}
95

  
96
sub db {
97
  my $string = $_[0];
98
  my $lookup = $lxoffice_package_names{$_[0]} ||
99
      plurify($lxoffice_package_names{singlify($_[0])});
100

  
101
  for my $thing ($string, $lookup) {
102

  
103
    # best guess? its already the name. like part. camelize it first
104
    my $class = "SL::DB::" . camelify($thing);
105
    return $class if defined *{ $class. '::' };
106

  
107
    # next, someone wants a manager and pluralized.
108
    my $manager = "SL::DB::Manager::" . singlify(camelify($thing));
109
    return $manager if defined *{ $manager . '::' };
110
  }
111

  
112
  die "Can't resolve '$string' as a database model, sorry. Did you perhaps forgot to load it?";
113
}
114

  
115
sub camelify {
116
  my ($str) = @_;
117
  $str =~ s/_+(.)/uc($1)/ge;
118
  ucfirst $str;
119
}
120

  
121
sub snakify {
122
  my ($str) = @_;
123
  $str =~ s/(?<!^)\u(.)/'_' . lc($1)/ge;
124
  lcfirst $str;
125
}
126

  
127
sub plurify {
128
  my ($str) = @_;
129
  $str . 's';
130
}
131

  
132
sub singlify {
133
  my ($str) = @_;
134
  local $/ = 's';
135
  chomp $str;
136
  $str;
137
}
138

  
139
1;
140

  
141
__END__
142

  
143
=head1 NAME
144

  
145
SL::DB::Helpers::Mappings - Rose Table <-> Model mapping information
146

  
147
=head1 SYNOPSIS
148

  
149
  use SL::DB::Helpers::Mappings qw(@blacklist %table2model);
150

  
151
=head1 DESCRIPTION
152

  
153
This modul stores table <-> model mappings used by the
154
L<scripts/rose_auto_create_model.pl> script.  If you add a new table that has
155
custom mappings, add it here.
156

  
157
=head2 db
158

  
159
A special function provided here is E<db>. Without it you'd have to write:
160

  
161
  my $part = SL::DB::Part->new(id => 1234);
162
  my @all_parts = SL::DB::Manager::Part->get_all;
163

  
164
with them it becomes:
165

  
166
  my $part = db('part')->new(id => 123);
167
  my @all_parts = db('parts')->get_all;
168

  
169
You don't have to care about add that SL::DB:: incantation anymore. Also, a
170
simple s at the end will get you the associated Manager class.
171

  
172
db is written to try to make sense of what you give it, but if all fails, it
173
will die with an error.
174

  
175
=head1 BUGS
176

  
177
nothing yet
178

  
179
=head1 SEE ALSO
180

  
181
L<scripts/rose_auto_create_model.pl>
182

  
183
=head1 AUTHOR
184

  
185
Sven Schöling <s.schoeling@linet-services.de>
186

  
187
=cut
SL/DB/Helper/Metadata.pm
1
package SL::DB::Helper::Metadata;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::Metadata;
6
use SL::DB::Helper::ConventionManager;
7

  
8
use base qw(Rose::DB::Object::Metadata);
9

  
10
sub convention_manager_class {
11
  return 'SL::DB::Helper::ConventionManager';
12
}
13

  
14
sub default_manager_base_class {
15
  return 'SL::DB::Helper::Manager';
16
}
17

  
18
sub initialize {
19
  my $self = shift;
20
  $self->make_attr_auto_helpers unless $self->is_initialized;
21
  $self->SUPER::initialize(@_);
22
}
23

  
24
sub make_attr_helpers {
25
  my ($self, %params) = @_;
26
  SL::DB::Helper::Attr::make($self->class, %params);
27
}
28

  
29
sub make_attr_auto_helpers {
30
  my ($self) = @_;
31
  SL::DB::Helper::Attr::auto_make($self->class);
32
}
33

  
34
1;
SL/DB/Helper/Sorted.pm
1
package SL::DB::Helper::Sorted;
2

  
3
use strict;
4

  
5
require Exporter;
6
our @ISA    = qw(Exporter);
7
our @EXPORT = qw(get_all_sorted make_sort_string);
8

  
9
my %sort_spec;
10

  
11
sub make_sort_string {
12
  my ($class, %params) = @_;
13

  
14
  my $sort_spec        = _get_sort_spec($class);
15

  
16
  my $sort_dir         = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
17
  my $sort_dir_str     = $sort_dir ? 'ASC' : 'DESC';
18

  
19
  my $sort_by          = $params{sort_by};
20
  $sort_by             = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
21

  
22
  my $nulls_str        = '';
23
  if ($sort_spec->{nulls}) {
24
    $nulls_str = ref($sort_spec->{nulls}) ? ($sort_spec->{nulls}->{$sort_by} || $sort_spec->{nulls}->{default}) : $sort_spec->{nulls};
25
    $nulls_str = " NULLS ${nulls_str}" if $nulls_str;
26
  }
27

  
28
  my $sort_by_str = $sort_spec->{columns}->{$sort_by};
29
  $sort_by_str    = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
30
  $sort_by_str    = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
31

  
32
  return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
33
}
34

  
35
sub get_all_sorted {
36
  my ($class, %params) = @_;
37
  my $sort_str         = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
38

  
39
  return $class->get_all(sort_by => $sort_str, %params);
40
}
41

  
42
sub _get_sort_spec {
43
  my ($class) = @_;
44
  return $sort_spec{$class} ||= _make_sort_spec($class);
45
}
46

  
47
sub _make_sort_spec {
48
  my ($class) = @_;
49

  
50
  my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
51

  
52
  my $meta = $class->object_class->meta;
53

  
54
  if (!$sort_spec{default}) {
55
    my @primary_keys = $meta->primary_key;
56
    $sort_spec{default} = [ "" . $primary_keys[0], 1 ];
57
  }
58

  
59
  $sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
60

  
61
  if ($sort_spec{columns}->{SIMPLE}) {
62
    my $table = $meta->table;
63

  
64
    if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
65
      map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
66
      delete $sort_spec{columns}->{SIMPLE};
67
    } else {
68
      map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
69
    }
70
  }
71

  
72
  return \%sort_spec;
73
}
74

  
75
1;
76

  
77
__END__
78

  
79
=encoding utf8
80

  
81
=head1 NAME
82

  
83
SL::DB::Helper::Sorted - Mixin for a manager class that handles
84
sorting of database records
85

  
86
=head1 SYNOPSIS
87

  
88
  package SL::DB::Manager::Message;
89

  
90
  use SL::DB::Helper::Sorted;
91

  
92
  sub _sort_spec {
93
    return ( columns => { recipient_id => [ 'CASE
94
                                             WHEN recipient_group_id IS NULL THEN lower(recipient.name)
95
                                             ELSE                                 lower(recipient_group.name)
96
                                             END',                                      ],
97
                          sender_id    => [ 'lower(sender.name)',                       ],
98
                          created_at   => [ 'created_at',                               ],
99
                          subject      => [ 'lower(subject)',                           ],
100
                          status       => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
101
                        },
102
             default => [ 'status', 1 ],
103
             nulls   => { default => 'LAST',
104
                          subject => 'FIRST',
105
                        }
106
           );
107
  }
108

  
109
  package SL::Controller::Message;
110

  
111
  sub action_list {
112
    my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by  => $::form->{sort_by},
113
                                                            sort_dir => $::form->{sort_dir});
114
  }
115

  
116
=head1 CLASS FUNCTIONS
117

  
118
=over 4
119

  
120
=item C<make_sort_string %params>
121

  
122
Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
123
SQL string suitable for sorting. The package this package is mixed
124
into has to provide a method L</_sort_spec> that returns a hash whose
125
structure is explained below. That hash is authoritive in which
126
columns may be sorted, which column to sort by by default and how to
127
handle C<NULL> values.
128

  
129
Returns the SQL string in scalar context. In array context it returns
130
three values: the actual column it sorts by (suitable for another call
131
to L</make_sort_string>), the actual sort direction (either 0 or 1)
132
and the SQL string.
133

  
134
=item C<get_all_sorted %params>
135

  
136
Returns C<< $class->get_all >> with C<sort_by> set to the value
137
returned by c<< $class->make_sort_string(%params) >>.
138

  
139
=back
140

  
141
=head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
142

  
143
=over 4
144

  
145
=item C<_sort_spec>
146

  
147
This method is actually not part of this package but can be provided
148
by the package this helper is mixed into. If it isn't then all columns
149
of the corresponding table (as returned by the model's meta data) will
150
be eligible for sorting.
151

  
152
Returns a hash with the following keys:
153

  
154
=over 2
155

  
156
=item C<default>
157

  
158
A two-element array containing the name and direction by which to sort
159
in default cases. Example:
160

  
161
  default => [ 'name', 1 ],
162

  
163
Defaults to the table's primary key column (the first column if the
164
primary key is composited).
165

  
166
=item C<columns>
167

  
168
A hash reference. Its keys are column names, and its values are SQL
169
strings by which to sort. Example:
170

  
171
  columns => { SIMPLE                  => [ 'transaction_description', 'orddate' ],
172
               the_date                => 'CASE WHEN oe.quotation THEN oe.quodate ELSE oe.orddate END',
173
               customer_name           => 'lower(customer.name)',
174
             },
175

  
176
If sorting by a column is requested that is not a key in this hash
177
then the default column name will be used.
178

  
179
The value can be either a scalar or an array reference. If it's the
180
latter then both the sort direction as well as the null handling will
181
be appended to each of its members.
182

  
183
The special key C<SIMPLE> can be a scalar or an array reference. If it
184
is an array reference then it contains column names that are mapped
185
1:1 onto the table's columns. If it is the scalar 'ALL' then all
186
columns in that model's meta data are mapped 1:1 unless the C<columns>
187
hash already contains a key for that column.
188

  
189
If C<columns> is missing then all columns of the model will be
190
eligible for sorting. The list of columns is looked up in the model's
191
meta data.
192

  
193
=item C<nulls>
194

  
195
Either a scalar or a hash reference determining where C<NULL> values
196
will be sorted. If undefined then the decision is left to the
197
database.
198

  
199
If it is a scalar then all the same value will be used for all
200
classes. The value is either C<FIRST> or C<LAST>.
201

  
202
If it is a hash reference then its keys are column names (not SQL
203
names). The values are either C<FIRST> or C<LAST>. If a column name is
204
not found in this hash then the special keu C<default> will be looked
205
up and used if it is found.
206

  
207
Example:
208

  
209
  nulls => { transaction_description => 'FIRST',
210
             customer_name           => 'FIRST',
211
             default                 => 'LAST',
212
           },
213

  
214
=back
215

  
216
=back
217

  
218
=head1 BUGS
219

  
220
Nothing here yet.
221

  
222
=head1 AUTHOR
223

  
224
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
225

  
226
=cut
SL/DB/Helpers/ALL.pm
1
package SL::DB::Helpers::ALL;
2

  
3
use strict;
4

  
5
use SL::DB::AccTrans;
6
use SL::DB::AccTransaction;
7
use SL::DB::Assembly;
8
use SL::DB::AuditTrail;
9
use SL::DB::BankAccount;
10
use SL::DB::Bin;
11
use SL::DB::Buchungsgruppe;
12
use SL::DB::Business;
13
use SL::DB::Chart;
14
use SL::DB::Contact;
15
use SL::DB::CustomVariable;
16
use SL::DB::CustomVariableConfig;
17
use SL::DB::CustomVariableValidity;
18
use SL::DB::Customer;
19
use SL::DB::CustomerTax;
20
use SL::DB::Datev;
21
use SL::DB::Default;
22
use SL::DB::DeliveryOrder;
23
use SL::DB::DeliveryOrderItem;
24
use SL::DB::DeliveryOrderItemsStock;
25
use SL::DB::Department;
26
use SL::DB::DptTrans;
27
use SL::DB::Draft;
28
use SL::DB::Dunning;
29
use SL::DB::DunningConfig;
30
use SL::DB::Employee;
31
use SL::DB::Exchangerate;
32
use SL::DB::Finanzamt;
33
use SL::DB::FollowUp;
34
use SL::DB::FollowUpAccess;
35
use SL::DB::FollowUpLink;
36
use SL::DB::GLTransaction;
37
use SL::DB::GenericTranslation;
38
use SL::DB::Gifi;
39
use SL::DB::History;
40
use SL::DB::Inventory;
41
use SL::DB::Invoice;
42
use SL::DB::InvoiceItem;
43
use SL::DB::Language;
44
use SL::DB::License;
45
use SL::DB::LicenseInvoice;
46
use SL::DB::MakeModel;
47
use SL::DB::Note;
48
use SL::DB::Order;
49
use SL::DB::OrderItem;
50
use SL::DB::Part;
51
use SL::DB::PartsGroup;
52
use SL::DB::PartsTax;
53
use SL::DB::PaymentTerm;
54
use SL::DB::PriceFactor;
55
use SL::DB::Pricegroup;
56
use SL::DB::Prices;
57
use SL::DB::Printer;
58
use SL::DB::Project;
59
use SL::DB::PurchaseInvoice;
60
use SL::DB::RMA;
61
use SL::DB::RMAItem;
62
use SL::DB::RecordLink;
63
use SL::DB::SchemaInfo;
64
use SL::DB::SepaExport;
65
use SL::DB::SepaExportItem;
66
use SL::DB::Shipto;
67
use SL::DB::Status;
68
use SL::DB::Tax;
69
use SL::DB::TaxKey;
70
use SL::DB::TaxZone;
71
use SL::DB::TodoUserConfig;
72
use SL::DB::TransferType;
73
use SL::DB::Translation;
74
use SL::DB::TranslationPaymentTerm;
75
use SL::DB::Unit;
76
use SL::DB::UnitsLanguage;
77
use SL::DB::Vendor;
78
use SL::DB::VendorTax;
79
use SL::DB::Warehouse;
80

  
81
1;
82

  
83
__END__
84

  
85
=pod
86

  
87
=head1 NAME
88

  
89
SL::DB::Helpers::ALL: Dependency-only package for all SL::DB::* modules
90

  
91
=head1 SYNOPSIS
92

  
93
  use SL::DB::Helpers::ALL;
94

  
95
=head1 DESCRIPTION
96

  
97
This module depends on all modules in SL/DB/*.pm for the convenience
98
of being able to write a simple \C<use SL::DB::Helpers::ALL> and
99
having everything loaded. This is supposed to be used only in the
100
Lx-Office console. Normal modules should C<use> only the modules they
101
actually need.
102

  
103
=head1 AUTHOR
104

  
105
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
106

  
107
=cut
SL/DB/Helpers/Attr.pm
1
package SL::DB::Helper::Attr;
2

  
3
use strict;
4

  
5
sub auto_make {
6
  my ($package, %params) = @_;
7

  
8
  for my $col ($package->meta->columns) {
9
    next if $col->primary_key_position; # don't make attr helper for primary keys
10
    _make_by_type($package, $col->name, $col->type);
11
  }
12

  
13
  return $package;
14
}
15

  
16
sub make {
17
  my ($package, %params) = @_;
18

  
19
  for my $name (keys %params) {
20
    my @types = ref $params{$name} eq 'ARRAY' ? @{ $params{$name} } : ($params{$name});
21
    for my $type (@types) {
22
      _make_by_type($package, $name, $type);
23
    }
24
  }
25
  return $package;
26
}
27

  
28

  
29

  
30
sub _make_by_type {
31
  my ($package, $name, $type) = @_;
32
  _as_number ($package, $name, places => -2) if $type =~ /numeric | real | float/xi;
33
  _as_percent($package, $name, places =>  0) if $type =~ /numeric | real | float/xi;
34
  _as_number ($package, $name, places =>  0) if $type =~ /int/xi;
35
  _as_date   ($package, $name)               if $type =~ /date | timestamp/xi;
36
}
37

  
38
sub _as_number {
39
  my $package     = shift;
40
  my $attribute   = shift;
41
  my %params      = @_;
42

  
43
  $params{places} = 2 if !defined($params{places});
44

  
45
  no strict 'refs';
46
  *{ $package . '::' . $attribute . '_as_number' } = sub {
47
    my ($self, $string) = @_;
48

  
49
    $self->$attribute($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
50

  
51
    return $::form->format_amount(\%::myconfig, $self->$attribute, $params{places});
52
  };
53
}
54

  
55
sub _as_percent {
56
  my $package     = shift;
57
  my $attribute   = shift;
58
  my %params      = @_;
59

  
60
  $params{places} = 2 if !defined($params{places});
61

  
62
  no strict 'refs';
63
  *{ $package . '::' . $attribute . '_as_percent' } = sub {
64
    my ($self, $string) = @_;
65

  
66
    $self->$attribute($::form->parse_amount(\%::myconfig, $string) / 100) if @_ > 1;
67

  
68
    return $::form->format_amount(\%::myconfig, 100 * $self->$attribute, $params{places});
69
  };
70

  
71
  return 1;
72
}
73

  
74
sub _as_date {
75
  my $package     = shift;
76
  my $attribute   = shift;
77
  my %params      = @_;
78

  
79
  no strict 'refs';
80
  *{ $package . '::' . $attribute . '_as_date' } = sub {
81
    my ($self, $string) = @_;
82

  
83
    if (@_ > 1) {
84
      if ($string) {
85
        my ($yy, $mm, $dd) = $::locale->parse_date(\%::myconfig, $string);
86
        $self->$attribute(DateTime->new(year => $yy, month => $mm, day => $dd));
87
      } else {
88
        $self->$attribute(undef);
89
      }
90
    }
91

  
92
    return $self->$attribute
93
      ? $::locale->reformat_date(
94
          { dateformat => 'yy-mm-dd' },
95
          ( $self->$attribute eq 'now'
96
             ? DateTime->now
97
             : $self->$attribute
98
          )->ymd,
99
          $::myconfig{dateformat}
100
        )
101
      : undef;
102
  };
103

  
104
  return 1;
105
}
106

  
107
1;
108

  
109

  
110
1;
111

  
112
__END__
113

  
114
=head1 NAME
115

  
116
SL::DB::Helpers::Attr - attribute helpers
117

  
118
=head1 SYNOPSIS
119

  
120
  use SL::DB::Helpers::Attr;
121
  SL::DB::Helpers::Attr::make($class,
122
    method_name => 'numeric(15,5)',
123
    datemethod  => 'date'
124
  );
125
  SL::DB::Helpers::Attr::auto_make($class);
126

  
127
=head1 DESCRIPTION
128

  
129
=head1 FUNCTIONS
130

  
131
=head1 BUGS
132

  
133
=head1 AUTHOR
134

  
135
=cut
SL/DB/Helpers/ConventionManager.pm
1
package SL::DB::Helpers::ConventionManager;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::ConventionManager;
6

  
7
use base qw(Rose::DB::Object::ConventionManager);
8

  
9
sub auto_manager_class_name {
10
  my $self         = shift;
11
  my $object_class = shift || $self->meta->class;
12

  
13
  my @parts        = split m/::/, $object_class;
14
  my $last         = pop @parts;
15

  
16
  return join('::', @parts, 'Manager', $last);
17
}
18

  
19
# Base name used for 'make_manager_class', e.g. 'get_all',
20
# 'update_all'
21
sub auto_manager_base_name {
22
  return 'all';
23
}
24

  
25
sub auto_manager_base_class {
26
  return 'SL::DB::Helpers::Manager';
27
}
28

  
29
1;
SL/DB/Helpers/Manager.pm
1
package SL::DB::Helpers::Manager;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::Manager;
6
use base qw(Rose::DB::Object::Manager);
7

  
8
sub make_manager_methods {
9
  my $class  = shift;
10
  my @params = scalar(@_) ? @_ : qw(all);
11
  return $class->SUPER::make_manager_methods(@params);
12
}
13

  
14
sub find_by {
15
  my $class = shift;
16

  
17
  return if !@_;
18
  return $class->get_all(query => [ @_ ], limit => 1)->[0];
19
}
20

  
21
sub get_first {
22
  shift->get_all(
23
    limit => 1,
24
  )->[0];
25
}
26

  
27
1;
SL/DB/Helpers/Mappings.pm
1
package SL::DB::Helpers::Mappings;
2

  
3
use utf8;
4
use strict;
5

  
6
# these will not be managed as Rose::DB models, because they are not normalized,
7
# significant changes are needed to get them done, or they were done by CRM.
8
my @lxoffice_blacklist_permanent = qw(
9
  leads
10
);
11

  
12
# these are not managed _yet_, but will hopefully at some point.
13
# if you are confident that one of these works, remove it here.
14
my @lxoffice_blacklist_temp = qw(
15
);
16

  
17
my @lxoffice_blacklist = (@lxoffice_blacklist_permanent, @lxoffice_blacklist_temp);
18

  
19
# map table names to their models.
20
# unlike rails we have no singular<->plural magic.
21
# remeber: tables should be named as the plural of the model name.
22
my %lxoffice_package_names = (
23
  acc_trans                      => 'acc_transaction',
24
  audittrail                     => 'audit_trail',
25
  ar                             => 'invoice',
26
  ap                             => 'purchase_invoice',
27
  bank_accounts                  => 'bank_account',
28
  buchungsgruppen                => 'buchungsgruppe',
29
  contacts                       => 'contact',
30
  custom_variable_configs        => 'custom_variable_config',
31
  custom_variables               => 'custom_variable',
32
  custom_variables_validity      => 'custom_variable_validity',
33
  customertax                    => 'customer_tax',
34
  datev                          => 'datev',
35
  defaults                       => 'default',
36
  delivery_orders                => 'delivery_order',
37
  delivery_order_items           => 'delivery_order_item',
38
  department                     => 'department',
39
  dpt_trans                      => 'dpt_trans',
40
  drafts                         => 'draft',
41
  dunning                        => 'dunning',
42
  dunning_config                 => 'dunning_config',
43
  employee                       => 'employee',
44
  exchangerate                   => 'exchangerate',
45
  finanzamt                      => 'finanzamt',
46
  follow_up_access               => 'follow_up_access',
47
  follow_up_links                => 'follow_up_link',
48
  follow_ups                     => 'follow_up',
49
  generic_translations           => 'generic_translation',
50
  gifi                           => 'gifi',
51
  gl                             => 'GLTransaction',
52
  history_erp                    => 'history',
53
  inventory                      => 'inventory',
54
  invoice                        => 'invoice_item',
55
  language                       => 'language',
56
  license                        => 'license',
57
  licenseinvoice                 => 'license_invoice',
58
  makemodel                      => 'make_model',
59
  notes                          => 'note',
60
  orderitems                     => 'order_item',
61
  oe                             => 'order',
62
  parts                          => 'part',
63
  partsgroup                     => 'parts_group',
64
  partstax                       => 'parts_tax',
65
  payment_terms                  => 'payment_term',
66
  prices                         => 'prices',
67
  price_factors                  => 'price_factor',
68
  pricegroup                     => 'pricegroup',
69
  printers                       => 'Printer',
70
  record_links                   => 'record_link',
71
  rma                            => 'RMA',
72
  rmaitems                       => 'RMA_item',
73
  sepa_export                    => 'sepa_export',
74
  sepa_export_items              => 'sepa_export_item',
75
  schema_info                    => 'schema_info',
76
  status                         => 'status',
77
  tax                            => 'tax',
78
  taxkeys                        => 'tax_key',
79
  tax_zones                      => 'tax_zone',
80
  todo_user_config               => 'todo_user_config',
81
  translation                    => 'translation',
82
  translation_payment_terms      => 'translation_payment_term',
83
  units                          => 'unit',
84
  units_language                 => 'units_language',
85
  vendortax                      => 'vendor_tax',
86
);
87

  
88
sub get_blacklist {
89
  return LXOFFICE => \@lxoffice_blacklist;
90
}
91

  
92
sub get_package_names {
93
  return LXOFFICE => \%lxoffice_package_names;
94
}
95

  
96
sub db {
97
  my $string = $_[0];
98
  my $lookup = $lxoffice_package_names{$_[0]} ||
99
      plurify($lxoffice_package_names{singlify($_[0])});
100

  
101
  for my $thing ($string, $lookup) {
102

  
103
    # best guess? its already the name. like part. camelize it first
104
    my $class = "SL::DB::" . camelify($thing);
105
    return $class if defined *{ $class. '::' };
106

  
107
    # next, someone wants a manager and pluralized.
108
    my $manager = "SL::DB::Manager::" . singlify(camelify($thing));
109
    return $manager if defined *{ $manager . '::' };
110
  }
111

  
112
  die "Can't resolve '$string' as a database model, sorry. Did you perhaps forgot to load it?";
113
}
114

  
115
sub camelify {
116
  my ($str) = @_;
117
  $str =~ s/_+(.)/uc($1)/ge;
118
  ucfirst $str;
119
}
120

  
121
sub snakify {
122
  my ($str) = @_;
123
  $str =~ s/(?<!^)\u(.)/'_' . lc($1)/ge;
124
  lcfirst $str;
125
}
126

  
127
sub plurify {
128
  my ($str) = @_;
129
  $str . 's';
130
}
131

  
132
sub singlify {
133
  my ($str) = @_;
134
  local $/ = 's';
135
  chomp $str;
136
  $str;
137
}
138

  
139
1;
140

  
141
__END__
142

  
143
=head1 NAME
144

  
145
SL::DB::Helpers::Mappings - Rose Table <-> Model mapping information
146

  
147
=head1 SYNOPSIS
148

  
149
  use SL::DB::Helpers::Mappings qw(@blacklist %table2model);
150

  
151
=head1 DESCRIPTION
152

  
153
This modul stores table <-> model mappings used by the
154
L<scripts/rose_auto_create_model.pl> script.  If you add a new table that has
155
custom mappings, add it here.
156

  
157
=head2 db
158

  
159
A special function provided here is E<db>. Without it you'd have to write:
160

  
161
  my $part = SL::DB::Part->new(id => 1234);
162
  my @all_parts = SL::DB::Manager::Part->get_all;
163

  
164
with them it becomes:
165

  
166
  my $part = db('part')->new(id => 123);
167
  my @all_parts = db('parts')->get_all;
168

  
169
You don't have to care about add that SL::DB:: incantation anymore. Also, a
170
simple s at the end will get you the associated Manager class.
171

  
172
db is written to try to make sense of what you give it, but if all fails, it
173
will die with an error.
174

  
175
=head1 BUGS
176

  
177
nothing yet
178

  
179
=head1 SEE ALSO
180

  
181
L<scripts/rose_auto_create_model.pl>
182

  
183
=head1 AUTHOR
184

  
185
Sven Schöling <s.schoeling@linet-services.de>
186

  
187
=cut
SL/DB/Helpers/Metadata.pm
1
package SL::DB::Helpers::Metadata;
2

  
3
use strict;
4

  
5
use Rose::DB::Object::Metadata;
6
use SL::DB::Helpers::ConventionManager;
7

  
8
use base qw(Rose::DB::Object::Metadata);
9

  
10
sub convention_manager_class {
11
  return 'SL::DB::Helpers::ConventionManager';
12
}
13

  
14
sub default_manager_base_class {
15
  return 'SL::DB::Helpers::Manager';
16
}
17

  
18
sub initialize {
19
  my $self = shift;
20
  $self->make_attr_auto_helpers unless $self->is_initialized;
21
  $self->SUPER::initialize(@_);
22
}
23

  
24
sub make_attr_helpers {
25
  my ($self, %params) = @_;
26
  SL::DB::Helper::Attr::make($self->class, %params);
27
}
28

  
29
sub make_attr_auto_helpers {
30
  my ($self) = @_;
31
  SL::DB::Helper::Attr::auto_make($self->class);
32
}
33

  
34
1;
SL/DB/Helpers/Sorted.pm
1
package SL::DB::Helpers::Sorted;
2

  
3
use strict;
4

  
5
require Exporter;
6
our @ISA    = qw(Exporter);
7
our @EXPORT = qw(get_all_sorted make_sort_string);
8

  
9
my %sort_spec;
10

  
11
sub make_sort_string {
12
  my ($class, %params) = @_;
13

  
14
  my $sort_spec        = _get_sort_spec($class);
15

  
16
  my $sort_dir         = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
17
  my $sort_dir_str     = $sort_dir ? 'ASC' : 'DESC';
18

  
19
  my $sort_by          = $params{sort_by};
20
  $sort_by             = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
21

  
22
  my $nulls_str        = '';
23
  if ($sort_spec->{nulls}) {
24
    $nulls_str = ref($sort_spec->{nulls}) ? ($sort_spec->{nulls}->{$sort_by} || $sort_spec->{nulls}->{default}) : $sort_spec->{nulls};
25
    $nulls_str = " NULLS ${nulls_str}" if $nulls_str;
26
  }
27

  
28
  my $sort_by_str = $sort_spec->{columns}->{$sort_by};
29
  $sort_by_str    = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
30
  $sort_by_str    = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
31

  
32
  return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
33
}
34

  
35
sub get_all_sorted {
36
  my ($class, %params) = @_;
37
  my $sort_str         = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
38

  
39
  return $class->get_all(sort_by => $sort_str, %params);
40
}
41

  
42
sub _get_sort_spec {
43
  my ($class) = @_;
44
  return $sort_spec{$class} ||= _make_sort_spec($class);
45
}
46

  
47
sub _make_sort_spec {
48
  my ($class) = @_;
49

  
50
  my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
51

  
52
  my $meta = $class->object_class->meta;
53

  
54
  if (!$sort_spec{default}) {
55
    my @primary_keys = $meta->primary_key;
56
    $sort_spec{default} = [ "" . $primary_keys[0], 1 ];
57
  }
58

  
59
  $sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
60

  
61
  if ($sort_spec{columns}->{SIMPLE}) {
62
    my $table = $meta->table;
63

  
64
    if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
65
      map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
66
      delete $sort_spec{columns}->{SIMPLE};
67
    } else {
68
      map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
69
    }
70
  }
71

  
72
  return \%sort_spec;
73
}
74

  
75
1;
76

  
77
__END__
78

  
79
=encoding utf8
80

  
81
=head1 NAME
82

  
83
SL::DB::Helpers::Sorted - Mixin for a manager class that handles
84
sorting of database records
85

  
86
=head1 SYNOPSIS
87

  
88
  package SL::DB::Manager::Message;
89

  
90
  use SL::DB::Helpers::Sorted;
91

  
92
  sub _sort_spec {
93
    return ( columns => { recipient_id => [ 'CASE
94
                                             WHEN recipient_group_id IS NULL THEN lower(recipient.name)
95
                                             ELSE                                 lower(recipient_group.name)
96
                                             END',                                      ],
97
                          sender_id    => [ 'lower(sender.name)',                       ],
98
                          created_at   => [ 'created_at',                               ],
99
                          subject      => [ 'lower(subject)',                           ],
100
                          status       => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
101
                        },
102
             default => [ 'status', 1 ],
103
             nulls   => { default => 'LAST',
104
                          subject => 'FIRST',
105
                        }
106
           );
107
  }
108

  
109
  package SL::Controller::Message;
110

  
111
  sub action_list {
112
    my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by  => $::form->{sort_by},
113
                                                            sort_dir => $::form->{sort_dir});
114
  }
115

  
116
=head1 CLASS FUNCTIONS
117

  
118
=over 4
119

  
120
=item C<make_sort_string %params>
121

  
122
Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
123
SQL string suitable for sorting. The package this package is mixed
124
into has to provide a method L</_sort_spec> that returns a hash whose
125
structure is explained below. That hash is authoritive in which
126
columns may be sorted, which column to sort by by default and how to
127
handle C<NULL> values.
128

  
129
Returns the SQL string in scalar context. In array context it returns
130
three values: the actual column it sorts by (suitable for another call
131
to L</make_sort_string>), the actual sort direction (either 0 or 1)
132
and the SQL string.
133

  
134
=item C<get_all_sorted %params>
135

  
136
Returns C<< $class->get_all >> with C<sort_by> set to the value
137
returned by c<< $class->make_sort_string(%params) >>.
138

  
139
=back
140

  
141
=head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
142

  
143
=over 4
144

  
145
=item C<_sort_spec>
146

  
147
This method is actually not part of this package but can be provided
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff