Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision ac552280

Von Moritz Bunkus vor etwa 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
package SL::DB::Helper::ALL;
use strict;
use SL::DB::AccTrans;
use SL::DB::AccTransaction;
use SL::DB::Assembly;
use SL::DB::AuditTrail;
use SL::DB::BankAccount;
use SL::DB::Bin;
use SL::DB::Buchungsgruppe;
use SL::DB::Business;
use SL::DB::Chart;
use SL::DB::Contact;
use SL::DB::CustomVariable;
use SL::DB::CustomVariableConfig;
use SL::DB::CustomVariableValidity;
use SL::DB::Customer;
use SL::DB::CustomerTax;
use SL::DB::Datev;
use SL::DB::Default;
use SL::DB::DeliveryOrder;
use SL::DB::DeliveryOrderItem;
use SL::DB::DeliveryOrderItemsStock;
use SL::DB::Department;
use SL::DB::DptTrans;
use SL::DB::Draft;
use SL::DB::Dunning;
use SL::DB::DunningConfig;
use SL::DB::Employee;
use SL::DB::Exchangerate;
use SL::DB::Finanzamt;
use SL::DB::FollowUp;
use SL::DB::FollowUpAccess;
use SL::DB::FollowUpLink;
use SL::DB::GLTransaction;
use SL::DB::GenericTranslation;
use SL::DB::Gifi;
use SL::DB::History;
use SL::DB::Inventory;
use SL::DB::Invoice;
use SL::DB::InvoiceItem;
use SL::DB::Language;
use SL::DB::License;
use SL::DB::LicenseInvoice;
use SL::DB::MakeModel;
use SL::DB::Note;
use SL::DB::Order;
use SL::DB::OrderItem;
use SL::DB::Part;
use SL::DB::PartsGroup;
use SL::DB::PartsTax;
use SL::DB::PaymentTerm;
use SL::DB::PriceFactor;
use SL::DB::Pricegroup;
use SL::DB::Prices;
use SL::DB::Printer;
use SL::DB::Project;
use SL::DB::PurchaseInvoice;
use SL::DB::RMA;
use SL::DB::RMAItem;
use SL::DB::RecordLink;
use SL::DB::SchemaInfo;
use SL::DB::SepaExport;
use SL::DB::SepaExportItem;
use SL::DB::Shipto;
use SL::DB::Status;
use SL::DB::Tax;
use SL::DB::TaxKey;
use SL::DB::TaxZone;
use SL::DB::TodoUserConfig;
use SL::DB::TransferType;
use SL::DB::Translation;
use SL::DB::TranslationPaymentTerm;
use SL::DB::Unit;
use SL::DB::UnitsLanguage;
use SL::DB::Vendor;
use SL::DB::VendorTax;
use SL::DB::Warehouse;
1;
__END__
=pod
=head1 NAME
SL::DB::Helper::ALL: Dependency-only package for all SL::DB::* modules
=head1 SYNOPSIS
use SL::DB::Helper::ALL;
=head1 DESCRIPTION
This module depends on all modules in SL/DB/*.pm for the convenience
of being able to write a simple \C<use SL::DB::Helper::ALL> and
having everything loaded. This is supposed to be used only in the
Lx-Office console. Normal modules should C<use> only the modules they
actually need.
=head1 AUTHOR
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
=cut
SL/DB/Helper/Attr.pm
package SL::DB::Helper::Attr;
use strict;
sub auto_make {
my ($package, %params) = @_;
for my $col ($package->meta->columns) {
next if $col->primary_key_position; # don't make attr helper for primary keys
_make_by_type($package, $col->name, $col->type);
}
return $package;
}
sub make {
my ($package, %params) = @_;
for my $name (keys %params) {
my @types = ref $params{$name} eq 'ARRAY' ? @{ $params{$name} } : ($params{$name});
for my $type (@types) {
_make_by_type($package, $name, $type);
}
}
return $package;
}
sub _make_by_type {
my ($package, $name, $type) = @_;
_as_number ($package, $name, places => -2) if $type =~ /numeric | real | float/xi;
_as_percent($package, $name, places => 0) if $type =~ /numeric | real | float/xi;
_as_number ($package, $name, places => 0) if $type =~ /int/xi;
_as_date ($package, $name) if $type =~ /date | timestamp/xi;
}
sub _as_number {
my $package = shift;
my $attribute = shift;
my %params = @_;
$params{places} = 2 if !defined($params{places});
no strict 'refs';
*{ $package . '::' . $attribute . '_as_number' } = sub {
my ($self, $string) = @_;
$self->$attribute($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
return $::form->format_amount(\%::myconfig, $self->$attribute, $params{places});
};
}
sub _as_percent {
my $package = shift;
my $attribute = shift;
my %params = @_;
$params{places} = 2 if !defined($params{places});
no strict 'refs';
*{ $package . '::' . $attribute . '_as_percent' } = sub {
my ($self, $string) = @_;
$self->$attribute($::form->parse_amount(\%::myconfig, $string) / 100) if @_ > 1;
return $::form->format_amount(\%::myconfig, 100 * $self->$attribute, $params{places});
};
return 1;
}
sub _as_date {
my $package = shift;
my $attribute = shift;
my %params = @_;
no strict 'refs';
*{ $package . '::' . $attribute . '_as_date' } = sub {
my ($self, $string) = @_;
if (@_ > 1) {
if ($string) {
my ($yy, $mm, $dd) = $::locale->parse_date(\%::myconfig, $string);
$self->$attribute(DateTime->new(year => $yy, month => $mm, day => $dd));
} else {
$self->$attribute(undef);
}
}
return $self->$attribute
? $::locale->reformat_date(
{ dateformat => 'yy-mm-dd' },
( $self->$attribute eq 'now'
? DateTime->now
: $self->$attribute
)->ymd,
$::myconfig{dateformat}
)
: undef;
};
return 1;
}
1;
1;
__END__
=head1 NAME
SL::DB::Helper::Attr - attribute helpers
=head1 SYNOPSIS
use SL::DB::Helper::Attr;
SL::DB::Helper::Attr::make($class,
method_name => 'numeric(15,5)',
datemethod => 'date'
);
SL::DB::Helper::Attr::auto_make($class);
=head1 DESCRIPTION
=head1 FUNCTIONS
=head1 BUGS
=head1 AUTHOR
=cut
SL/DB/Helper/ConventionManager.pm
package SL::DB::Helper::ConventionManager;
use strict;
use Rose::DB::Object::ConventionManager;
use base qw(Rose::DB::Object::ConventionManager);
sub auto_manager_class_name {
my $self = shift;
my $object_class = shift || $self->meta->class;
my @parts = split m/::/, $object_class;
my $last = pop @parts;
return join('::', @parts, 'Manager', $last);
}
# Base name used for 'make_manager_class', e.g. 'get_all',
# 'update_all'
sub auto_manager_base_name {
return 'all';
}
sub auto_manager_base_class {
return 'SL::DB::Helper::Manager';
}
1;
SL/DB/Helper/Manager.pm
package SL::DB::Helper::Manager;
use strict;
use Rose::DB::Object::Manager;
use base qw(Rose::DB::Object::Manager);
sub make_manager_methods {
my $class = shift;
my @params = scalar(@_) ? @_ : qw(all);
return $class->SUPER::make_manager_methods(@params);
}
sub find_by {
my $class = shift;
return if !@_;
return $class->get_all(query => [ @_ ], limit => 1)->[0];
}
sub get_first {
shift->get_all(
limit => 1,
)->[0];
}
1;
SL/DB/Helper/Mappings.pm
package SL::DB::Helpers::Mappings;
use utf8;
use strict;
# these will not be managed as Rose::DB models, because they are not normalized,
# significant changes are needed to get them done, or they were done by CRM.
my @lxoffice_blacklist_permanent = qw(
leads
);
# these are not managed _yet_, but will hopefully at some point.
# if you are confident that one of these works, remove it here.
my @lxoffice_blacklist_temp = qw(
);
my @lxoffice_blacklist = (@lxoffice_blacklist_permanent, @lxoffice_blacklist_temp);
# map table names to their models.
# unlike rails we have no singular<->plural magic.
# remeber: tables should be named as the plural of the model name.
my %lxoffice_package_names = (
acc_trans => 'acc_transaction',
audittrail => 'audit_trail',
ar => 'invoice',
ap => 'purchase_invoice',
bank_accounts => 'bank_account',
buchungsgruppen => 'buchungsgruppe',
contacts => 'contact',
custom_variable_configs => 'custom_variable_config',
custom_variables => 'custom_variable',
custom_variables_validity => 'custom_variable_validity',
customertax => 'customer_tax',
datev => 'datev',
defaults => 'default',
delivery_orders => 'delivery_order',
delivery_order_items => 'delivery_order_item',
department => 'department',
dpt_trans => 'dpt_trans',
drafts => 'draft',
dunning => 'dunning',
dunning_config => 'dunning_config',
employee => 'employee',
exchangerate => 'exchangerate',
finanzamt => 'finanzamt',
follow_up_access => 'follow_up_access',
follow_up_links => 'follow_up_link',
follow_ups => 'follow_up',
generic_translations => 'generic_translation',
gifi => 'gifi',
gl => 'GLTransaction',
history_erp => 'history',
inventory => 'inventory',
invoice => 'invoice_item',
language => 'language',
license => 'license',
licenseinvoice => 'license_invoice',
makemodel => 'make_model',
notes => 'note',
orderitems => 'order_item',
oe => 'order',
parts => 'part',
partsgroup => 'parts_group',
partstax => 'parts_tax',
payment_terms => 'payment_term',
prices => 'prices',
price_factors => 'price_factor',
pricegroup => 'pricegroup',
printers => 'Printer',
record_links => 'record_link',
rma => 'RMA',
rmaitems => 'RMA_item',
sepa_export => 'sepa_export',
sepa_export_items => 'sepa_export_item',
schema_info => 'schema_info',
status => 'status',
tax => 'tax',
taxkeys => 'tax_key',
tax_zones => 'tax_zone',
todo_user_config => 'todo_user_config',
translation => 'translation',
translation_payment_terms => 'translation_payment_term',
units => 'unit',
units_language => 'units_language',
vendortax => 'vendor_tax',
);
sub get_blacklist {
return LXOFFICE => \@lxoffice_blacklist;
}
sub get_package_names {
return LXOFFICE => \%lxoffice_package_names;
}
sub db {
my $string = $_[0];
my $lookup = $lxoffice_package_names{$_[0]} ||
plurify($lxoffice_package_names{singlify($_[0])});
for my $thing ($string, $lookup) {
# best guess? its already the name. like part. camelize it first
my $class = "SL::DB::" . camelify($thing);
return $class if defined *{ $class. '::' };
# next, someone wants a manager and pluralized.
my $manager = "SL::DB::Manager::" . singlify(camelify($thing));
return $manager if defined *{ $manager . '::' };
}
die "Can't resolve '$string' as a database model, sorry. Did you perhaps forgot to load it?";
}
sub camelify {
my ($str) = @_;
$str =~ s/_+(.)/uc($1)/ge;
ucfirst $str;
}
sub snakify {
my ($str) = @_;
$str =~ s/(?<!^)\u(.)/'_' . lc($1)/ge;
lcfirst $str;
}
sub plurify {
my ($str) = @_;
$str . 's';
}
sub singlify {
my ($str) = @_;
local $/ = 's';
chomp $str;
$str;
}
1;
__END__
=head1 NAME
SL::DB::Helpers::Mappings - Rose Table <-> Model mapping information
=head1 SYNOPSIS
use SL::DB::Helpers::Mappings qw(@blacklist %table2model);
=head1 DESCRIPTION
This modul stores table <-> model mappings used by the
L<scripts/rose_auto_create_model.pl> script. If you add a new table that has
custom mappings, add it here.
=head2 db
A special function provided here is E<db>. Without it you'd have to write:
my $part = SL::DB::Part->new(id => 1234);
my @all_parts = SL::DB::Manager::Part->get_all;
with them it becomes:
my $part = db('part')->new(id => 123);
my @all_parts = db('parts')->get_all;
You don't have to care about add that SL::DB:: incantation anymore. Also, a
simple s at the end will get you the associated Manager class.
db is written to try to make sense of what you give it, but if all fails, it
will die with an error.
=head1 BUGS
nothing yet
=head1 SEE ALSO
L<scripts/rose_auto_create_model.pl>
=head1 AUTHOR
Sven Schöling <s.schoeling@linet-services.de>
=cut
SL/DB/Helper/Metadata.pm
package SL::DB::Helper::Metadata;
use strict;
use Rose::DB::Object::Metadata;
use SL::DB::Helper::ConventionManager;
use base qw(Rose::DB::Object::Metadata);
sub convention_manager_class {
return 'SL::DB::Helper::ConventionManager';
}
sub default_manager_base_class {
return 'SL::DB::Helper::Manager';
}
sub initialize {
my $self = shift;
$self->make_attr_auto_helpers unless $self->is_initialized;
$self->SUPER::initialize(@_);
}
sub make_attr_helpers {
my ($self, %params) = @_;
SL::DB::Helper::Attr::make($self->class, %params);
}
sub make_attr_auto_helpers {
my ($self) = @_;
SL::DB::Helper::Attr::auto_make($self->class);
}
1;
SL/DB/Helper/Sorted.pm
package SL::DB::Helper::Sorted;
use strict;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(get_all_sorted make_sort_string);
my %sort_spec;
sub make_sort_string {
my ($class, %params) = @_;
my $sort_spec = _get_sort_spec($class);
my $sort_dir = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
my $sort_dir_str = $sort_dir ? 'ASC' : 'DESC';
my $sort_by = $params{sort_by};
$sort_by = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
my $nulls_str = '';
if ($sort_spec->{nulls}) {
$nulls_str = ref($sort_spec->{nulls}) ? ($sort_spec->{nulls}->{$sort_by} || $sort_spec->{nulls}->{default}) : $sort_spec->{nulls};
$nulls_str = " NULLS ${nulls_str}" if $nulls_str;
}
my $sort_by_str = $sort_spec->{columns}->{$sort_by};
$sort_by_str = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
$sort_by_str = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
}
sub get_all_sorted {
my ($class, %params) = @_;
my $sort_str = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
return $class->get_all(sort_by => $sort_str, %params);
}
sub _get_sort_spec {
my ($class) = @_;
return $sort_spec{$class} ||= _make_sort_spec($class);
}
sub _make_sort_spec {
my ($class) = @_;
my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
my $meta = $class->object_class->meta;
if (!$sort_spec{default}) {
my @primary_keys = $meta->primary_key;
$sort_spec{default} = [ "" . $primary_keys[0], 1 ];
}
$sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
if ($sort_spec{columns}->{SIMPLE}) {
my $table = $meta->table;
if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
delete $sort_spec{columns}->{SIMPLE};
} else {
map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
}
}
return \%sort_spec;
}
1;
__END__
=encoding utf8
=head1 NAME
SL::DB::Helper::Sorted - Mixin for a manager class that handles
sorting of database records
=head1 SYNOPSIS
package SL::DB::Manager::Message;
use SL::DB::Helper::Sorted;
sub _sort_spec {
return ( columns => { recipient_id => [ 'CASE
WHEN recipient_group_id IS NULL THEN lower(recipient.name)
ELSE lower(recipient_group.name)
END', ],
sender_id => [ 'lower(sender.name)', ],
created_at => [ 'created_at', ],
subject => [ 'lower(subject)', ],
status => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
},
default => [ 'status', 1 ],
nulls => { default => 'LAST',
subject => 'FIRST',
}
);
}
package SL::Controller::Message;
sub action_list {
my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by => $::form->{sort_by},
sort_dir => $::form->{sort_dir});
}
=head1 CLASS FUNCTIONS
=over 4
=item C<make_sort_string %params>
Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
SQL string suitable for sorting. The package this package is mixed
into has to provide a method L</_sort_spec> that returns a hash whose
structure is explained below. That hash is authoritive in which
columns may be sorted, which column to sort by by default and how to
handle C<NULL> values.
Returns the SQL string in scalar context. In array context it returns
three values: the actual column it sorts by (suitable for another call
to L</make_sort_string>), the actual sort direction (either 0 or 1)
and the SQL string.
=item C<get_all_sorted %params>
Returns C<< $class->get_all >> with C<sort_by> set to the value
returned by c<< $class->make_sort_string(%params) >>.
=back
=head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
=over 4
=item C<_sort_spec>
This method is actually not part of this package but can be provided
by the package this helper is mixed into. If it isn't then all columns
of the corresponding table (as returned by the model's meta data) will
be eligible for sorting.
Returns a hash with the following keys:
=over 2
=item C<default>
A two-element array containing the name and direction by which to sort
in default cases. Example:
default => [ 'name', 1 ],
Defaults to the table's primary key column (the first column if the
primary key is composited).
=item C<columns>
A hash reference. Its keys are column names, and its values are SQL
strings by which to sort. Example:
columns => { SIMPLE => [ 'transaction_description', 'orddate' ],
the_date => 'CASE WHEN oe.quotation THEN oe.quodate ELSE oe.orddate END',
customer_name => 'lower(customer.name)',
},
If sorting by a column is requested that is not a key in this hash
then the default column name will be used.
The value can be either a scalar or an array reference. If it's the
latter then both the sort direction as well as the null handling will
be appended to each of its members.
The special key C<SIMPLE> can be a scalar or an array reference. If it
is an array reference then it contains column names that are mapped
1:1 onto the table's columns. If it is the scalar 'ALL' then all
columns in that model's meta data are mapped 1:1 unless the C<columns>
hash already contains a key for that column.
If C<columns> is missing then all columns of the model will be
eligible for sorting. The list of columns is looked up in the model's
meta data.
=item C<nulls>
Either a scalar or a hash reference determining where C<NULL> values
will be sorted. If undefined then the decision is left to the
database.
If it is a scalar then all the same value will be used for all
classes. The value is either C<FIRST> or C<LAST>.
If it is a hash reference then its keys are column names (not SQL
names). The values are either C<FIRST> or C<LAST>. If a column name is
not found in this hash then the special keu C<default> will be looked
up and used if it is found.
Example:
nulls => { transaction_description => 'FIRST',
customer_name => 'FIRST',
default => 'LAST',
},
=back
=back
=head1 BUGS
Nothing here yet.
=head1 AUTHOR
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
=cut
SL/DB/Helpers/ALL.pm
package SL::DB::Helpers::ALL;
use strict;
use SL::DB::AccTrans;
use SL::DB::AccTransaction;
use SL::DB::Assembly;
use SL::DB::AuditTrail;
use SL::DB::BankAccount;
use SL::DB::Bin;
use SL::DB::Buchungsgruppe;
use SL::DB::Business;
use SL::DB::Chart;
use SL::DB::Contact;
use SL::DB::CustomVariable;
use SL::DB::CustomVariableConfig;
use SL::DB::CustomVariableValidity;
use SL::DB::Customer;
use SL::DB::CustomerTax;
use SL::DB::Datev;
use SL::DB::Default;
use SL::DB::DeliveryOrder;
use SL::DB::DeliveryOrderItem;
use SL::DB::DeliveryOrderItemsStock;
use SL::DB::Department;
use SL::DB::DptTrans;
use SL::DB::Draft;
use SL::DB::Dunning;
use SL::DB::DunningConfig;
use SL::DB::Employee;
use SL::DB::Exchangerate;
use SL::DB::Finanzamt;
use SL::DB::FollowUp;
use SL::DB::FollowUpAccess;
use SL::DB::FollowUpLink;
use SL::DB::GLTransaction;
use SL::DB::GenericTranslation;
use SL::DB::Gifi;
use SL::DB::History;
use SL::DB::Inventory;
use SL::DB::Invoice;
use SL::DB::InvoiceItem;
use SL::DB::Language;
use SL::DB::License;
use SL::DB::LicenseInvoice;
use SL::DB::MakeModel;
use SL::DB::Note;
use SL::DB::Order;
use SL::DB::OrderItem;
use SL::DB::Part;
use SL::DB::PartsGroup;
use SL::DB::PartsTax;
use SL::DB::PaymentTerm;
use SL::DB::PriceFactor;
use SL::DB::Pricegroup;
use SL::DB::Prices;
use SL::DB::Printer;
use SL::DB::Project;
use SL::DB::PurchaseInvoice;
use SL::DB::RMA;
use SL::DB::RMAItem;
use SL::DB::RecordLink;
use SL::DB::SchemaInfo;
use SL::DB::SepaExport;
use SL::DB::SepaExportItem;
use SL::DB::Shipto;
use SL::DB::Status;
use SL::DB::Tax;
use SL::DB::TaxKey;
use SL::DB::TaxZone;
use SL::DB::TodoUserConfig;
use SL::DB::TransferType;
use SL::DB::Translation;
use SL::DB::TranslationPaymentTerm;
use SL::DB::Unit;
use SL::DB::UnitsLanguage;
use SL::DB::Vendor;
use SL::DB::VendorTax;
use SL::DB::Warehouse;
1;
__END__
=pod
=head1 NAME
SL::DB::Helpers::ALL: Dependency-only package for all SL::DB::* modules
=head1 SYNOPSIS
use SL::DB::Helpers::ALL;
=head1 DESCRIPTION
This module depends on all modules in SL/DB/*.pm for the convenience
of being able to write a simple \C<use SL::DB::Helpers::ALL> and
having everything loaded. This is supposed to be used only in the
Lx-Office console. Normal modules should C<use> only the modules they
actually need.
=head1 AUTHOR
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
=cut
SL/DB/Helpers/Attr.pm
package SL::DB::Helper::Attr;
use strict;
sub auto_make {
my ($package, %params) = @_;
for my $col ($package->meta->columns) {
next if $col->primary_key_position; # don't make attr helper for primary keys
_make_by_type($package, $col->name, $col->type);
}
return $package;
}
sub make {
my ($package, %params) = @_;
for my $name (keys %params) {
my @types = ref $params{$name} eq 'ARRAY' ? @{ $params{$name} } : ($params{$name});
for my $type (@types) {
_make_by_type($package, $name, $type);
}
}
return $package;
}
sub _make_by_type {
my ($package, $name, $type) = @_;
_as_number ($package, $name, places => -2) if $type =~ /numeric | real | float/xi;
_as_percent($package, $name, places => 0) if $type =~ /numeric | real | float/xi;
_as_number ($package, $name, places => 0) if $type =~ /int/xi;
_as_date ($package, $name) if $type =~ /date | timestamp/xi;
}
sub _as_number {
my $package = shift;
my $attribute = shift;
my %params = @_;
$params{places} = 2 if !defined($params{places});
no strict 'refs';
*{ $package . '::' . $attribute . '_as_number' } = sub {
my ($self, $string) = @_;
$self->$attribute($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
return $::form->format_amount(\%::myconfig, $self->$attribute, $params{places});
};
}
sub _as_percent {
my $package = shift;
my $attribute = shift;
my %params = @_;
$params{places} = 2 if !defined($params{places});
no strict 'refs';
*{ $package . '::' . $attribute . '_as_percent' } = sub {
my ($self, $string) = @_;
$self->$attribute($::form->parse_amount(\%::myconfig, $string) / 100) if @_ > 1;
return $::form->format_amount(\%::myconfig, 100 * $self->$attribute, $params{places});
};
return 1;
}
sub _as_date {
my $package = shift;
my $attribute = shift;
my %params = @_;
no strict 'refs';
*{ $package . '::' . $attribute . '_as_date' } = sub {
my ($self, $string) = @_;
if (@_ > 1) {
if ($string) {
my ($yy, $mm, $dd) = $::locale->parse_date(\%::myconfig, $string);
$self->$attribute(DateTime->new(year => $yy, month => $mm, day => $dd));
} else {
$self->$attribute(undef);
}
}
return $self->$attribute
? $::locale->reformat_date(
{ dateformat => 'yy-mm-dd' },
( $self->$attribute eq 'now'
? DateTime->now
: $self->$attribute
)->ymd,
$::myconfig{dateformat}
)
: undef;
};
return 1;
}
1;
1;
__END__
=head1 NAME
SL::DB::Helpers::Attr - attribute helpers
=head1 SYNOPSIS
use SL::DB::Helpers::Attr;
SL::DB::Helpers::Attr::make($class,
method_name => 'numeric(15,5)',
datemethod => 'date'
);
SL::DB::Helpers::Attr::auto_make($class);
=head1 DESCRIPTION
=head1 FUNCTIONS
=head1 BUGS
=head1 AUTHOR
=cut
SL/DB/Helpers/ConventionManager.pm
package SL::DB::Helpers::ConventionManager;
use strict;
use Rose::DB::Object::ConventionManager;
use base qw(Rose::DB::Object::ConventionManager);
sub auto_manager_class_name {
my $self = shift;
my $object_class = shift || $self->meta->class;
my @parts = split m/::/, $object_class;
my $last = pop @parts;
return join('::', @parts, 'Manager', $last);
}
# Base name used for 'make_manager_class', e.g. 'get_all',
# 'update_all'
sub auto_manager_base_name {
return 'all';
}
sub auto_manager_base_class {
return 'SL::DB::Helpers::Manager';
}
1;
SL/DB/Helpers/Manager.pm
package SL::DB::Helpers::Manager;
use strict;
use Rose::DB::Object::Manager;
use base qw(Rose::DB::Object::Manager);
sub make_manager_methods {
my $class = shift;
my @params = scalar(@_) ? @_ : qw(all);
return $class->SUPER::make_manager_methods(@params);
}
sub find_by {
my $class = shift;
return if !@_;
return $class->get_all(query => [ @_ ], limit => 1)->[0];
}
sub get_first {
shift->get_all(
limit => 1,
)->[0];
}
1;
SL/DB/Helpers/Mappings.pm
package SL::DB::Helpers::Mappings;
use utf8;
use strict;
# these will not be managed as Rose::DB models, because they are not normalized,
# significant changes are needed to get them done, or they were done by CRM.
my @lxoffice_blacklist_permanent = qw(
leads
);
# these are not managed _yet_, but will hopefully at some point.
# if you are confident that one of these works, remove it here.
my @lxoffice_blacklist_temp = qw(
);
my @lxoffice_blacklist = (@lxoffice_blacklist_permanent, @lxoffice_blacklist_temp);
# map table names to their models.
# unlike rails we have no singular<->plural magic.
# remeber: tables should be named as the plural of the model name.
my %lxoffice_package_names = (
acc_trans => 'acc_transaction',
audittrail => 'audit_trail',
ar => 'invoice',
ap => 'purchase_invoice',
bank_accounts => 'bank_account',
buchungsgruppen => 'buchungsgruppe',
contacts => 'contact',
custom_variable_configs => 'custom_variable_config',
custom_variables => 'custom_variable',
custom_variables_validity => 'custom_variable_validity',
customertax => 'customer_tax',
datev => 'datev',
defaults => 'default',
delivery_orders => 'delivery_order',
delivery_order_items => 'delivery_order_item',
department => 'department',
dpt_trans => 'dpt_trans',
drafts => 'draft',
dunning => 'dunning',
dunning_config => 'dunning_config',
employee => 'employee',
exchangerate => 'exchangerate',
finanzamt => 'finanzamt',
follow_up_access => 'follow_up_access',
follow_up_links => 'follow_up_link',
follow_ups => 'follow_up',
generic_translations => 'generic_translation',
gifi => 'gifi',
gl => 'GLTransaction',
history_erp => 'history',
inventory => 'inventory',
invoice => 'invoice_item',
language => 'language',
license => 'license',
licenseinvoice => 'license_invoice',
makemodel => 'make_model',
notes => 'note',
orderitems => 'order_item',
oe => 'order',
parts => 'part',
partsgroup => 'parts_group',
partstax => 'parts_tax',
payment_terms => 'payment_term',
prices => 'prices',
price_factors => 'price_factor',
pricegroup => 'pricegroup',
printers => 'Printer',
record_links => 'record_link',
rma => 'RMA',
rmaitems => 'RMA_item',
sepa_export => 'sepa_export',
sepa_export_items => 'sepa_export_item',
schema_info => 'schema_info',
status => 'status',
tax => 'tax',
taxkeys => 'tax_key',
tax_zones => 'tax_zone',
todo_user_config => 'todo_user_config',
translation => 'translation',
translation_payment_terms => 'translation_payment_term',
units => 'unit',
units_language => 'units_language',
vendortax => 'vendor_tax',
);
sub get_blacklist {
return LXOFFICE => \@lxoffice_blacklist;
}
sub get_package_names {
return LXOFFICE => \%lxoffice_package_names;
}
sub db {
my $string = $_[0];
my $lookup = $lxoffice_package_names{$_[0]} ||
plurify($lxoffice_package_names{singlify($_[0])});
for my $thing ($string, $lookup) {
# best guess? its already the name. like part. camelize it first
my $class = "SL::DB::" . camelify($thing);
return $class if defined *{ $class. '::' };
# next, someone wants a manager and pluralized.
my $manager = "SL::DB::Manager::" . singlify(camelify($thing));
return $manager if defined *{ $manager . '::' };
}
die "Can't resolve '$string' as a database model, sorry. Did you perhaps forgot to load it?";
}
sub camelify {
my ($str) = @_;
$str =~ s/_+(.)/uc($1)/ge;
ucfirst $str;
}
sub snakify {
my ($str) = @_;
$str =~ s/(?<!^)\u(.)/'_' . lc($1)/ge;
lcfirst $str;
}
sub plurify {
my ($str) = @_;
$str . 's';
}
sub singlify {
my ($str) = @_;
local $/ = 's';
chomp $str;
$str;
}
1;
__END__
=head1 NAME
SL::DB::Helpers::Mappings - Rose Table <-> Model mapping information
=head1 SYNOPSIS
use SL::DB::Helpers::Mappings qw(@blacklist %table2model);
=head1 DESCRIPTION
This modul stores table <-> model mappings used by the
L<scripts/rose_auto_create_model.pl> script. If you add a new table that has
custom mappings, add it here.
=head2 db
A special function provided here is E<db>. Without it you'd have to write:
my $part = SL::DB::Part->new(id => 1234);
my @all_parts = SL::DB::Manager::Part->get_all;
with them it becomes:
my $part = db('part')->new(id => 123);
my @all_parts = db('parts')->get_all;
You don't have to care about add that SL::DB:: incantation anymore. Also, a
simple s at the end will get you the associated Manager class.
db is written to try to make sense of what you give it, but if all fails, it
will die with an error.
=head1 BUGS
nothing yet
=head1 SEE ALSO
L<scripts/rose_auto_create_model.pl>
=head1 AUTHOR
Sven Schöling <s.schoeling@linet-services.de>
=cut
SL/DB/Helpers/Metadata.pm
package SL::DB::Helpers::Metadata;
use strict;
use Rose::DB::Object::Metadata;
use SL::DB::Helpers::ConventionManager;
use base qw(Rose::DB::Object::Metadata);
sub convention_manager_class {
return 'SL::DB::Helpers::ConventionManager';
}
sub default_manager_base_class {
return 'SL::DB::Helpers::Manager';
}
sub initialize {
my $self = shift;
$self->make_attr_auto_helpers unless $self->is_initialized;
$self->SUPER::initialize(@_);
}
sub make_attr_helpers {
my ($self, %params) = @_;
SL::DB::Helper::Attr::make($self->class, %params);
}
sub make_attr_auto_helpers {
my ($self) = @_;
SL::DB::Helper::Attr::auto_make($self->class);
}
1;
SL/DB/Helpers/Sorted.pm
package SL::DB::Helpers::Sorted;
use strict;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(get_all_sorted make_sort_string);
my %sort_spec;
sub make_sort_string {
my ($class, %params) = @_;
my $sort_spec = _get_sort_spec($class);
my $sort_dir = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
my $sort_dir_str = $sort_dir ? 'ASC' : 'DESC';
my $sort_by = $params{sort_by};
$sort_by = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
my $nulls_str = '';
if ($sort_spec->{nulls}) {
$nulls_str = ref($sort_spec->{nulls}) ? ($sort_spec->{nulls}->{$sort_by} || $sort_spec->{nulls}->{default}) : $sort_spec->{nulls};
$nulls_str = " NULLS ${nulls_str}" if $nulls_str;
}
my $sort_by_str = $sort_spec->{columns}->{$sort_by};
$sort_by_str = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
$sort_by_str = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
}
sub get_all_sorted {
my ($class, %params) = @_;
my $sort_str = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
return $class->get_all(sort_by => $sort_str, %params);
}
sub _get_sort_spec {
my ($class) = @_;
return $sort_spec{$class} ||= _make_sort_spec($class);
}
sub _make_sort_spec {
my ($class) = @_;
my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
my $meta = $class->object_class->meta;
if (!$sort_spec{default}) {
my @primary_keys = $meta->primary_key;
$sort_spec{default} = [ "" . $primary_keys[0], 1 ];
}
$sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
if ($sort_spec{columns}->{SIMPLE}) {
my $table = $meta->table;
if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
delete $sort_spec{columns}->{SIMPLE};
} else {
map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
}
}
return \%sort_spec;
}
1;
__END__
=encoding utf8
=head1 NAME
SL::DB::Helpers::Sorted - Mixin for a manager class that handles
sorting of database records
=head1 SYNOPSIS
package SL::DB::Manager::Message;
use SL::DB::Helpers::Sorted;
sub _sort_spec {
return ( columns => { recipient_id => [ 'CASE
WHEN recipient_group_id IS NULL THEN lower(recipient.name)
ELSE lower(recipient_group.name)
END', ],
sender_id => [ 'lower(sender.name)', ],
created_at => [ 'created_at', ],
subject => [ 'lower(subject)', ],
status => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
},
default => [ 'status', 1 ],
nulls => { default => 'LAST',
subject => 'FIRST',
}
);
}
package SL::Controller::Message;
sub action_list {
my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by => $::form->{sort_by},
sort_dir => $::form->{sort_dir});
}
=head1 CLASS FUNCTIONS
=over 4
=item C<make_sort_string %params>
Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
SQL string suitable for sorting. The package this package is mixed
into has to provide a method L</_sort_spec> that returns a hash whose
structure is explained below. That hash is authoritive in which
columns may be sorted, which column to sort by by default and how to
handle C<NULL> values.
Returns the SQL string in scalar context. In array context it returns
three values: the actual column it sorts by (suitable for another call
to L</make_sort_string>), the actual sort direction (either 0 or 1)
and the SQL string.
=item C<get_all_sorted %params>
Returns C<< $class->get_all >> with C<sort_by> set to the value
returned by c<< $class->make_sort_string(%params) >>.
=back
=head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
=over 4
=item C<_sort_spec>
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