From: Sven Schöling Date: Wed, 14 Sep 2011 09:10:53 +0000 (+0200) Subject: CVars als Object Mixin. X-Git-Tag: release-2.7.0beta1~307 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=e1bf173bae820584e8bafdc01ebe2e7d7537a59c;p=kivitendo-erp.git CVars als Object Mixin. Conflicts: SL/DB/Customer.pm SL/DB/Part.pm SL/DB/Vendor.pm --- diff --git a/SL/DB/Contact.pm b/SL/DB/Contact.pm index 69d666fec..d9ebf93e7 100644 --- a/SL/DB/Contact.pm +++ b/SL/DB/Contact.pm @@ -6,6 +6,10 @@ package SL::DB::Contact; use strict; use SL::DB::MetaSetup::Contact; +use SL::DB::Helper::CustomVariables ( + module => 'Contacts', + cvars_alias => 1, +); # Creates get_all, get_all_count, get_all_iterator, delete_all and update_all. __PACKAGE__->meta->make_manager_class; diff --git a/SL/DB/CustomVariable.pm b/SL/DB/CustomVariable.pm index 6410e3f25..26c09cf5b 100644 --- a/SL/DB/CustomVariable.pm +++ b/SL/DB/CustomVariable.pm @@ -10,4 +10,14 @@ use SL::DB::MetaSetup::CustomVariable; # Creates get_all, get_all_count, get_all_iterator, delete_all and update_all. __PACKAGE__->meta->make_manager_class; +sub value { + my $self = $_[0]; + my $type = $self->config->type; + + goto &bool_value if $type eq 'boolean'; + goto ×tamp_value if $type eq 'timestamp'; + goto &number_value if $type eq 'number'; + goto &text_value; # text and select +} + 1; diff --git a/SL/DB/Customer.pm b/SL/DB/Customer.pm index 186ad9750..7cdfde6e4 100644 --- a/SL/DB/Customer.pm +++ b/SL/DB/Customer.pm @@ -5,6 +5,10 @@ use strict; use SL::DB::MetaSetup::Customer; use SL::DB::Manager::Customer; use SL::DB::Helper::TransNumberGenerator; +use SL::DB::Helper::CustomVariables ( + module => 'CT', + cvars_alias => 1, +); use SL::DB::VC; diff --git a/SL/DB/DeliveryOrderItem.pm b/SL/DB/DeliveryOrderItem.pm index 2f508d310..fbab5ef05 100644 --- a/SL/DB/DeliveryOrderItem.pm +++ b/SL/DB/DeliveryOrderItem.pm @@ -3,6 +3,13 @@ package SL::DB::DeliveryOrderItem; use strict; use SL::DB::MetaSetup::DeliveryOrderItem; +use SL::DB::Helper::CustomVariables ( + sub_module => 'orderitems', + cvars_alias => 1, + overloads => { + parts_id => 'SL::DB::Part', + }, +); __PACKAGE__->meta->make_manager_class; diff --git a/SL/DB/Helper/CustomVariables.pm b/SL/DB/Helper/CustomVariables.pm new file mode 100644 index 000000000..e8c7d6b03 --- /dev/null +++ b/SL/DB/Helper/CustomVariables.pm @@ -0,0 +1,267 @@ +package SL::DB::Helper::CustomVariables; + +use strict; +use Carp; +use Data::Dumper; +use List::Util qw(first); +use SL::DB::CustomVariableConfig; + +use constant META_CVARS => 'cvars_config'; + +sub import { + my ($class, %params) = @_; + my $caller_package = caller; + + # TODO: if module is empty, module overloading needs to take effect + # certain stuff may have more than one overload, odr even more than one type + defined $caller_package or croak 'need to be included from a caller reference'; + + $params{module} ||= _calc_modules_from_overloads(%params) if $params{overloads}; + $params{sub_module} ||= ''; + $params{id} ||= 'id'; + + $params{module} || $params{sub_module} or croak 'need param module or sub_module'; + + save_meta_info($caller_package, %params); + make_cvar_accessor($caller_package, %params); + make_cvar_alias($caller_package, %params) if $params{cvars_alias}; + make_cvar_by_configs($caller_package, %params); + make_cvar_by_name($caller_package, %params); +} + +sub save_meta_info { + my ($caller_package, %params) = @_; + + my $meta = $caller_package->meta; + return 0 if $meta->{META_CVARS()}; + + $meta->{META_CVARS()} = \%params; +} + +sub make_cvar_accessor { + my ($caller_package, %params) = @_; + + my @module_filter = $params{module} ? + ("config.module" => $params{module}) : + (); + + $caller_package->meta->add_relationships( + custom_variables => { + type => 'one to many', + class => 'SL::DB::CustomVariable', + column_map => { ($params{id} || 'id') => 'trans_id' }, + manager_args => { with_objects => 'config' }, + query_args => [ sub_module => $params{sub_module}, @module_filter ], + } + ); +} + +sub make_cvar_alias { + my ($caller_package) = @_; + no strict 'refs'; + *{ $caller_package . '::cvars' } = sub { + goto &{ $caller_package . '::custom_variables' }; + } +} + +# this is used for templates where you need to list every applicable config +# auto vivifies non existant cvar objects as necessary. +sub make_cvar_by_configs { + my ($caller_package, %params) = @_; + + no strict 'refs'; + *{ $caller_package . '::cvars_by_config' } = sub { + my ($self) = @_; + @_ > 1 and croak "not an accessor"; + + my $configs = _all_configs(%params); + my $cvars = $self->custom_variables; + my %cvars_by_config = map { $_->config_id => $_ } @$cvars; + + my @return = map { $cvars_by_config{$_->id} || _new_cvar($self, %params, config => $_) } @$configs; + + return \@return; + } +} + +# this is used for print templates where you need to refer to a variable by name +# TODO typically these were referred as prefix_'cvar'_name +sub make_cvar_by_name { + my ($caller_package, %params) = @_; + + no strict 'refs'; + *{ $caller_package . '::cvar_by_name' } = sub { + my ($self, $name) = @_; + + my $configs = _all_configs(%params); + my $cvars = $self->custom_variables; + my $config = first { $_->name eq $name } @$configs; + + croak "unknown cvar name $name" unless $config; + + my $cvar = first { $_->config_id eq $config->id } @$cvars; + + if (!$cvar) { + $cvar = _new_cvar($self, %params, config => $config); + $self->add_custom_variables($cvar); + } + + return $cvar; + } +} + +sub _all_configs { + my (%params) = @_; + $params{module} + ? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ]) + : SL::DB::Manager::CustomVariableConfig->get_all; +} + +sub _overload_by_module { + my ($module, %params) = @_; + + while (my ($fk, $class) = each %{ $params{overloads} }) { + return ($fk, $class) if $class->meta->{META_CVARS()}->{module} eq $module; + } + + croak "unknown overload, cannot resolve module $module"; +} + +sub _new_cvar { + my ($self, %params) = @_; + my $inherited_value; + # check overloading first + if ($params{sub_module}) { + my ($fk, $class) = _overload_by_module($params{config}->module, %params); + my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name); + $inherited_value = $base_cvar->value; + } + + my $cvar = SL::DB::CustomVariable->new( + config => $params{config}, + trans_id => $self->${ \ $params{id} }, + sub_module => $params{sub_module}, + ); + # value needs config + $inherited_value + ? $cvar->value($inherited_value) + : $cvar->value($params{config}->default_value); + return $cvar; +} + +sub _calc_modules_from_overloads { + my (%params) = @_; + my %modules; + + while (my ($fk, $class) = each %{ $params{overloads} }) { + eval "require $class"; # make sure the class is loaded + my $module = $class->meta->{META_CVARS()}->{module}; + next if ref $module; + $modules{$module} = 1; + } + + return [ keys %modules ]; +} + + +1; + +__END__ + +=encoding utf-8 + +=head1 NAME + +SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations + +=head1 SYNOPSIS + + # use in a primary class + use SL::DB::Helper::CustomVariables ( + module => 'IC', + cvars_alias => 1, + ); + + # use overloading in a secondary class + use SL::DB::Helper::CustomVariables ( + sub_module => 'orderitems', + cvars_alias => 1, + overloads => { + parts_id => 'SL::DB::Part', + } + ); + +=head1 DESCRIPTION + +This module provides methods to deal with named custom variables. Two concepts are understood. + +=head2 Primary CVar Classes + +Primary classes are those that feature cvars for themselves. Currently those +are Part, Contact, Customer and Vendor. cvars for these will get saved directly +for the object. + +=head2 Secondary CVar Classes + +Secondary classes inherit their cvars from member relationships. This is built +so that orders can save a copy of the cvars of their parts, customers and the +like to be immutable later on. + +Secondary classes may currently not have cvars of their own. + +=head1 INSTALLED METHODS + +=over 4 + +=item C + +This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it +like any other OneToMany relationship. + +=item C + +Alias to C. Will only be installed if C was +passed to import. + +=item C + +Thi will return a list of CVars with the following changes over the standard accessor: + +=over 4 + +=item * + +The list will be returned in the sorted order of the configs. + +=item * + +For every config exactly one CVar will be returned. + +=item * + +If no cvar was found for a config, a new one will be vivified, set to the +correct config, module etc, and registered into the object. + +=item * + +Vivified cvars for secondary classes will first try to find their base object +and use that value. If no such value or cvar is found the default value from +configs applies. + +=back + +This is useful if you need to list every possible CVar, like in CRUD masks. + +=item C + +Returns the CVar object for this object which matches the given internal name. +Useful for print templates. If the requested cvar is not present, it will be +vivified with the same rules as in C. + +=back + +=head1 AUTHOR + +Sven Schöling Es.schoeling@linet-services.deE + +=cut diff --git a/SL/DB/Invoice.pm b/SL/DB/Invoice.pm index 91ee14c85..0b7caa49d 100644 --- a/SL/DB/Invoice.pm +++ b/SL/DB/Invoice.pm @@ -15,6 +15,13 @@ use SL::DB::Helper::LinkedRecords; use SL::DB::Helper::PriceTaxCalculator; use SL::DB::Helper::PriceUpdater; use SL::DB::Helper::TransNumberGenerator; +use SL::DB::Helper::CustomVariables ( + sub_module => 'orderitems', + cvars_alias => 1, + overloads => { + parts_id => 'SL::DB::Part', + }, +); use SL::DB::AccTransaction; use SL::DB::Chart; use SL::DB::Employee; diff --git a/SL/DB/OrderItem.pm b/SL/DB/OrderItem.pm index 1e17d364c..9e045f74d 100644 --- a/SL/DB/OrderItem.pm +++ b/SL/DB/OrderItem.pm @@ -3,6 +3,13 @@ package SL::DB::OrderItem; use strict; use SL::DB::MetaSetup::OrderItem; +use SL::DB::Helper::CustomVariables ( + sub_module => 'orderitems', + cvars_alias => 1, + overloads => { + parts_id => 'SL::DB::Part', + }, +); __PACKAGE__->meta->add_relationship( part => { diff --git a/SL/DB/Part.pm b/SL/DB/Part.pm index fd53dd64e..1f7f7e637 100644 --- a/SL/DB/Part.pm +++ b/SL/DB/Part.pm @@ -10,6 +10,10 @@ use SL::DB::MetaSetup::Part; use SL::DB::Manager::Part; use SL::DB::Chart; use SL::DB::Helper::TransNumberGenerator; +use SL::DB::Helper::CustomVariables ( + module => 'IC', + cvars_alias => 1, +); __PACKAGE__->meta->add_relationships( unit_obj => { diff --git a/SL/DB/Vendor.pm b/SL/DB/Vendor.pm index 53ac297b5..35143668e 100644 --- a/SL/DB/Vendor.pm +++ b/SL/DB/Vendor.pm @@ -4,6 +4,10 @@ use strict; use SL::DB::MetaSetup::Vendor; use SL::DB::Helper::TransNumberGenerator; +use SL::DB::Helper::CustomVariables ( + module => 'CT', + cvars_alias => 1, +); use SL::DB::VC;