From 7f8599c0909affd56ad82fd842015bb51c6a3dbf Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Thu, 24 Jan 2013 12:35:38 +0100 Subject: [PATCH] Projektverwaltung auf Rose- und Controller-Code umgestellt --- SL/Controller/Project.pm | 290 ++++++++++++++++ SL/DB/Manager/Project.pm | 79 +++++ SL/DB/Project.pm | 59 +++- SL/Projects.pm | 244 -------------- bin/mozilla/arap.pl | 51 --- bin/mozilla/projects.pl | 316 ------------------ bin/mozilla/rp.pl | 35 +- locale/de/all | 16 +- menu.ini | 8 +- projects.pl | 1 - .../project_form.html => project/form.html} | 48 ++- templates/webpages/project/report_bottom.html | 2 + templates/webpages/project/search.html | 65 ++++ templates/webpages/projects/search.html | 91 ----- templates/webpages/rp/report.html | 6 +- 15 files changed, 532 insertions(+), 779 deletions(-) create mode 100644 SL/Controller/Project.pm create mode 100644 SL/DB/Manager/Project.pm delete mode 100644 SL/Projects.pm delete mode 100644 bin/mozilla/projects.pl delete mode 120000 projects.pl rename templates/webpages/{projects/project_form.html => project/form.html} (53%) create mode 100644 templates/webpages/project/report_bottom.html create mode 100644 templates/webpages/project/search.html delete mode 100644 templates/webpages/projects/search.html diff --git a/SL/Controller/Project.pm b/SL/Controller/Project.pm new file mode 100644 index 000000000..fa42007bd --- /dev/null +++ b/SL/Controller/Project.pm @@ -0,0 +1,290 @@ +package SL::Controller::Project; + +use strict; + +use parent qw(SL::Controller::Base); + +use Clone qw(clone); + +use SL::Controller::Helper::GetModels; +use SL::Controller::Helper::Paginated; +use SL::Controller::Helper::Sorted; +use SL::Controller::Helper::ParseFilter; +use SL::Controller::Helper::ReportGenerator; +use SL::CVar; +use SL::DB::Customer; +use SL::DB::Project; +use SL::Helper::Flash; +use SL::Locale::String; + +use Rose::Object::MakeMethods::Generic +( + scalar => [ qw(project db_args flat_filter) ], +); + +__PACKAGE__->run_before('check_auth'); +__PACKAGE__->run_before('load_project', only => [ qw(edit update destroy) ]); + +__PACKAGE__->get_models_url_params('flat_filter'); +__PACKAGE__->make_paginated( + MODEL => 'Project', + PAGINATE_ARGS => 'db_args', + ONLY => [ qw(list) ], +); + +__PACKAGE__->make_sorted( + MODEL => 'Project', + ONLY => [ qw(list) ], + + DEFAULT_BY => 'projectnumber', + DEFAULT_DIR => 1, + + customer => t8('Customer'), + description => t8('Description'), + projectnumber => t8('Project Number'), + type => t8('Type'), +); + + +# +# actions +# + +sub action_search { + my ($self) = @_; + + my %params; + + $params{CUSTOM_VARIABLES} = CVar->get_configs(module => 'Projects'); + ($params{CUSTOM_VARIABLES_FILTER_CODE}, $params{CUSTOM_VARIABLES_INCLUSION_CODE}) + = CVar->render_search_options(variables => $params{CUSTOM_VARIABLES}, + include_prefix => 'l_', + include_value => 'Y'); + + $self->render('project/search', %params); +} + +sub action_list { + my ($self) = @_; + + $self->setup_db_args_from_filter; + $self->flat_filter({ map { $_->{key} => $_->{value} } $::form->flatten_variables('filter') }); + # $self->make_filter_summary; + + $self->prepare_report; + + $self->{projects} = $self->get_models(%{ $self->db_args }); + + $self->list_objects; +} + +sub action_new { + my ($self) = @_; + + $self->project(SL::DB::Project->new); + $self->display_form(title => $::locale->text('Create a new project'), + callback => $::form->{callback} || $self->url_for(action => 'new')); +} + +sub action_edit { + my ($self) = @_; + $self->display_form(title => $::locale->text('Edit project #1', $self->project->projectnumber), + callback => $::form->{callback} || $self->url_for(action => 'edit', id => $self->project->id)); +} + +sub action_create { + my ($self) = @_; + + $self->project(SL::DB::Project->new); + $self->create_or_update; +} + +sub action_update { + my ($self) = @_; + $self->create_or_update; +} + +sub action_destroy { + my ($self) = @_; + + if (eval { $self->project->delete; 1; }) { + flash_later('info', $::locale->text('The project has been deleted.')); + } else { + flash_later('error', $::locale->text('The project is in use and cannot be deleted.')); + } + + $self->redirect_to(action => 'search'); +} + +# +# filters +# + +sub check_auth { + $::auth->assert('project_edit'); +} + +# +# helpers +# + +sub display_form { + my ($self, %params) = @_; + + $params{ALL_CUSTOMERS} = SL::DB::Manager::Customer->get_all_sorted(where => [ or => [ obsolete => 0, obsolete => undef, id => $self->project->customer_id ]]); + $params{CUSTOM_VARIABLES} = CVar->get_custom_variables(module => 'Projects', trans_id => $self->project->id); + CVar->render_inputs(variables => $params{CUSTOM_VARIABLES}) if @{ $params{CUSTOM_VARIABLES} }; + + $::request->{layout}->focus('#projectnumber'); + + $self->render('project/form', %params); +} + +sub create_or_update { + my $self = shift; + my $is_new = !$self->project->id; + my $params = delete($::form->{project}) || { }; + + delete $params->{id}; + $self->project->assign_attributes(%{ $params }); + + my @errors = $self->project->validate; + + if (@errors) { + flash('error', @errors); + $self->display_form(title => $is_new ? $::locale->text('Create a new project') : $::locale->text('Edit project'), + callback => $::form->{callback}); + return; + } + + $self->project->save; + + CVar->save_custom_variables( + dbh => $self->project->db->dbh, + module => 'Projects', + trans_id => $self->project->id, + variables => $::form, + always_valid => 1, + ); + + flash_later('info', $is_new ? $::locale->text('The project has been created.') : $::locale->text('The project has been saved.')); + + $self->redirect_to($::form->{callback} || (action => 'search')); +} + +sub load_project { + my ($self) = @_; + $self->project(SL::DB::Project->new(id => $::form->{id})->load); +} + +sub setup_db_args_from_filter { + my ($self) = @_; + + $self->{filter} = {}; + my %args = parse_filter( + $self->_pre_parse_filter($::form->{filter}, $self->{filter}), + with_objects => [ 'customer' ], + launder_to => $self->{filter}, + ); + + $self->db_args(\%args); +} + +# unfortunately ParseFilter can't handle compount filters. +# so we clone the original filter (still need that for serializing) +# rip out the options we know an replace them with the compound options. +# ParseFilter will take care of the prefixing then. +sub _pre_parse_filter { + my ($self, $orig_filter, $launder_to) = @_; + + return undef unless $orig_filter; + + my $filter = clone($orig_filter); + + $launder_to->{active} = delete $filter->{active}; + if ($orig_filter->{active} ne 'both') { + push @{ $filter->{and} }, $orig_filter->{active} eq 'active' ? (active => 1) : (or => [ active => 0, active => undef ]); + } + + $launder_to->{valid} = delete $filter->{valid}; + if ($orig_filter->{valid} ne 'both') { + push @{ $filter->{and} }, $orig_filter->{valid} eq 'valid' ? (valid => 1) : (or => [ valid => 0, valid => undef ]); + } + + $launder_to->{status} = delete $filter->{status}; + if ($orig_filter->{status} ne 'all') { + push @{ $filter->{and} }, SL::DB::Manager::Project->is_not_used_filter; + } + + return $filter; +} + +sub prepare_report { + my ($self) = @_; + + my $callback = $self->get_callback; + + my $report = SL::ReportGenerator->new(\%::myconfig, $::form); + $self->{report} = $report; + + my @columns = qw(projectnumber description customer active valid type); + my @sortable = qw(projectnumber description customer type); + + my %column_defs = ( + projectnumber => { obj_link => sub { $self->url_for(action => 'edit', id => $_[0]->id, callback => $callback) } }, + description => { obj_link => sub { $self->url_for(action => 'edit', id => $_[0]->id, callback => $callback) } }, + type => { }, + customer => { sub => sub { $_[0]->customer ? $_[0]->customer->name : '' } }, + active => { sub => sub { $_[0]->active ? $::locale->text('Active') : $::locale->text('Inactive') }, + text => $::locale->text('Active') }, + valid => { sub => sub { $_[0]->valid ? $::locale->text('Valid') : $::locale->text('Invalid') }, + text => $::locale->text('Valid') }, + ); + + map { $column_defs{$_}->{text} ||= $::locale->text( $self->get_sort_spec->{$_}->{title} ) } keys %column_defs; + + $report->set_options( + std_column_visibility => 1, + controller_class => 'Project', + output_format => 'HTML', + top_info_text => $::locale->text('Projects'), + raw_bottom_info_text => $self->render('project/report_bottom', { no_output => 1, partial => 1 }), + title => $::locale->text('Projects'), + allow_pdf_export => 1, + allow_csv_export => 1, + ); + $report->set_columns(%column_defs); + $report->set_column_order(@columns); + $report->set_export_options(qw(list filter)); + $report->set_options_from_form; + $self->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable); + + $self->disable_pagination if $report->{options}{output_format} =~ /^(pdf|csv)$/i; + + $self->{report_data} = { + column_defs => \%column_defs, + columns => \@columns, + }; +} + +sub list_objects { + my ($self) = @_; + my $column_defs = $self->{report_data}->{column_defs}; + + for my $obj (@{ $self->{projects} || [] }) { + my %data = map { + $_ => { + data => $column_defs->{$_}{sub} ? $column_defs->{$_}{sub}->($obj) + : $obj->can($_) ? $obj->$_ + : $obj->{$_}, + link => $column_defs->{$_}{obj_link} ? $column_defs->{$_}{obj_link}->($obj) : '', + }, + } @{ $self->{report_data}{columns} || {} }; + + $self->{report}->add_data(\%data); + } + + return $self->{report}->generate_with_headers; +} + +1; diff --git a/SL/DB/Manager/Project.pm b/SL/DB/Manager/Project.pm new file mode 100644 index 000000000..130e35ccd --- /dev/null +++ b/SL/DB/Manager/Project.pm @@ -0,0 +1,79 @@ +package SL::DB::Manager::Project; + +use strict; + +use parent qw(SL::DB::Helper::Manager); + +use SL::DB::Helper::Paginated; +use SL::DB::Helper::Sorted; + +sub object_class { 'SL::DB::Project' } + +__PACKAGE__->make_manager_methods; + +our %project_id_column_prefixes = ( + ar => 'global', + ap => 'global', + oe => 'global', + delivery_orders => 'global', +); + +our @tables_with_project_id_cols = qw(acc_trans ap ar delivery_order_items delivery_orders invoice oe orderitems rmaitems); + +sub _sort_spec { + return ( + default => [ 'projectnumber', 1 ], + columns => { + SIMPLE => 'ALL', + customer => 'customer.name', + }); +} + +sub is_not_used_filter { + my ($class, $prefix) = @_; + + my $query = join ' UNION ', map { + my $column = $project_id_column_prefixes{$_} . 'project_id'; + qq|SELECT DISTINCT ${column} FROM ${_} WHERE ${column} IS NOT NULL| + } @tables_with_project_id_cols; + + return ("!${prefix}id" => [ \"(${query})" ]); +} + +1; +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +SL::DB::Manager::Project - Manager for models for the 'project' table + +=head1 SYNOPSIS + +This is a standard Rose::DB::Manager based model manager and can be +used as such. + +=head1 FUNCTIONS + +=over 4 + +=item C + +Returns an array containing a partial filter suitable for the C +parameter that limits to projects that are not referenced from any +other database table. + +=back + +=head1 BUGS + +Nothing here yet. + +=head1 AUTHOR + +Moritz Bunkus Em.bunkus@linet-services.deE + +=cut diff --git a/SL/DB/Project.pm b/SL/DB/Project.pm index 1d1f1c4a8..ba6f12f0a 100644 --- a/SL/DB/Project.pm +++ b/SL/DB/Project.pm @@ -2,16 +2,52 @@ package SL::DB::Project; use strict; +use List::MoreUtils qw(any); + use SL::DB::MetaSetup::Project; +use SL::DB::Manager::Project; use SL::DB::Helper::CustomVariables( module => 'Project', cvars_alias => 1, ); -__PACKAGE__->meta->make_manager_class; __PACKAGE__->meta->initialize; +sub validate { + my ($self) = @_; + + my @errors; + push @errors, $::locale->text('The project number is missing.') if !$self->projectnumber; + push @errors, $::locale->text('The project number is already in use.') if !$self->is_projectnumber_unique; + push @errors, $::locale->text('The description is missing.') if !$self->description; + + return @errors; +} + +sub is_used { + my ($self) = @_; + + # Unsaved projects are never referenced. + return 0 unless $self->id; + + return any { + my $column = $SL::DB::Manager::Project::project_id_column_prefixes{$_} . 'project_id'; + $self->db->dbh->selectrow_arrayref(qq|SELECT EXISTS(SELECT * FROM ${_} WHERE ${column} = ?)|, undef, $self->id)->[0] + } @SL::DB::Manager::Project::tables_with_project_id_cols; +} + +sub is_projectnumber_unique { + my ($self) = @_; + + return 1 unless $self->projectnumber; + + my @filter = (projectnumber => $self->projectnumber); + @filter = (and => [ @filter, '!id' => $self->id ]) if $self->id; + + return !SL::DB::Manager::Project->get_first(where => \@filter); +} + 1; __END__ @@ -28,7 +64,26 @@ This is a standard Rose::DB::Object based model and can be used as one. =head1 FUNCTIONS -None so far. +=over 4 + +=item C + +Checks whether or not all fields are set to valid values so that the +object can be saved. If valid returns an empty list. Returns an array +of translated error message otherwise. + +=item C + +Checks whether or not the project is referenced from any other +database table. Returns a boolean value. + +=item C + +Returns trueish if the project number is not used for any other +project in the database. Also returns trueish if no project number has +been set yet. + +=back =head1 AUTHOR diff --git a/SL/Projects.pm b/SL/Projects.pm deleted file mode 100644 index 4c145bb57..000000000 --- a/SL/Projects.pm +++ /dev/null @@ -1,244 +0,0 @@ -#===================================================================== -# LX-Office ERP -# Copyright (C) 2004 -# Based on SQL-Ledger Version 2.1.9 -# Web http://www.lx-office.org -# -#===================================================================== -# SQL-Ledger Accounting -# Copyright (C) 1998-2002 -# -# Author: Dieter Simader -# Email: dsimader@sql-ledger.org -# Web: http://www.sql-ledger.org -# -# Contributors: -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -#====================================================================== -# -# Project module -# -#====================================================================== - -package Projects; - -use Data::Dumper; - -use SL::DBUtils; -use SL::CVar; - -use strict; - -my %project_id_column_prefixes = ("ar" => "global", - "ap" => "global", - "oe" => "global", - "delivery_orders" => "global"); - -my @tables_with_project_id_cols = qw(acc_trans - invoice - orderitems - rmaitems - ar - ap - oe - delivery_orders - delivery_order_items); - -sub search_projects { - $main::lxdebug->enter_sub(); - - my $self = shift; - my %params = @_; - - my $myconfig = \%main::myconfig; - my $form = $main::form; - - my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig); - - my (@filters, @values); - - foreach my $column (qw(projectnumber description)) { - if ($params{$column}) { - push @filters, "p.$column ILIKE ?"; - push @values, '%' . $params{$column} . '%'; - } - } - - if ($params{status} eq 'orphaned') { - my @sub_filters; - - foreach my $table (@tables_with_project_id_cols) { - push @sub_filters, qq|SELECT DISTINCT $project_id_column_prefixes{$table}project_id FROM $table - WHERE NOT $project_id_column_prefixes{$table}project_id ISNULL|; - } - - push @filters, "p.id NOT IN (" . join(" UNION ", @sub_filters) . ")"; - } - - if ($params{active} eq "active") { - push @filters, 'p.active'; - - } elsif ($params{active} eq "inactive") { - push @filters, 'NOT COALESCE(p.active, FALSE)'; - } - - if ($params{valid} eq "valid") { - push @filters, 'p.valid'; - - } elsif ($params{valid} eq "invalid") { - push @filters, 'NOT COALESCE(p.valid, FALSE)'; - } - - if ($params{customer}) { - push @filters, 'c.name ILIKE ?'; - push @values, '%' . $params{customer} . '%'; - } - - if ($params{type}) { - push @filters, 'p.type ILIKE ?'; - push @values, '%' . $params{type} . '%'; - } - - my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'Projects', - 'trans_id_field' => 'p.id', - 'filter' => $form); - - if ($cvar_where) { - push @filters, $cvar_where; - push @values, @cvar_values; - } - - - my $where = @filters ? 'WHERE ' . join(' AND ', map { "($_)" } @filters) : ''; - - my $sortorder = $params{sort} ? $params{sort} : "projectnumber"; - $sortorder =~ s/[^a-z_]//g; - my $query = qq|SELECT p.id, p.projectnumber, p.description, p.active, p.valid, p.type, - c.name AS customer - FROM project p - LEFT JOIN customer c ON (p.customer_id = c.id) - $where - ORDER BY $sortorder|; - - $form->{project_list} = selectall_hashref_query($form, $dbh, $query, @values); - - $main::lxdebug->leave_sub(); - - return scalar(@{ $form->{project_list} }); -} - -sub get_project { - $main::lxdebug->enter_sub(); - - my $self = shift; - my %params = @_; - - if (!$params{id}) { - $main::lxdebug->leave_sub(); - return { }; - } - - my $myconfig = \%main::myconfig; - my $form = $main::form; - - my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig); - - my $project = selectfirst_hashref_query($form, $dbh, qq|SELECT * FROM project WHERE id = ?|, conv_i($params{id})) || { }; - - if ($params{orphaned}) { - # check if it is orphaned - my (@values, $query); - - foreach my $table (@tables_with_project_id_cols) { - $query .= " + " if ($query); - $query .= qq|(SELECT COUNT(*) FROM $table - WHERE $project_id_column_prefixes{$table}project_id = ?) |; - push @values, conv_i($params{id}); - } - - $query = 'SELECT ' . $query; - - ($project->{orphaned}) = selectrow_query($form, $dbh, $query, @values); - $project->{orphaned} = !$project->{orphaned}; - } - - $main::lxdebug->leave_sub(); - - return $project; -} - -sub save_project { - $main::lxdebug->enter_sub(); - - my $self = shift; - my %params = @_; - - my $myconfig = \%main::myconfig; - my $form = $main::form; - - my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig); - - my @values; - - if (!$params{id}) { - ($params{id}) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('id')|); - do_query($form, $dbh, qq|INSERT INTO project (id) VALUES (?)|, conv_i($params{id})); - - $params{active} = 1; - } - - my $query = <save_custom_variables('dbh' => $dbh, - 'module' => 'Projects', - 'trans_id' => $params{id}, - 'variables' => $form, - 'always_valid' => 1); - - $dbh->commit(); - - $main::lxdebug->leave_sub(); - - return $params{id}; -} - -sub delete_project { - $main::lxdebug->enter_sub(); - - my $self = shift; - my %params = @_; - - Common::check_params(\%params, qw(id)); - - my $myconfig = \%main::myconfig; - my $form = $main::form; - - my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig); - - do_query($form, $dbh, qq|DELETE FROM project WHERE id = ?|, conv_i($params{id})); - - $dbh->commit(); - - $main::lxdebug->leave_sub(); -} - -1; diff --git a/bin/mozilla/arap.pl b/bin/mozilla/arap.pl index fe5fa2d27..347c152c7 100644 --- a/bin/mozilla/arap.pl +++ b/bin/mozilla/arap.pl @@ -30,8 +30,6 @@ # common routines for gl, ar, ap, is, ir, oe # -use SL::Projects; - use strict; # any custom scripts for this one @@ -298,54 +296,6 @@ sub _reset_salesman_id { $::form->{salesman_id} = $current_employee->id if $current_employee && exists $::form->{salesman_id}; } -sub check_project { - $main::lxdebug->enter_sub(); - - my $form = $main::form; - my $locale = $main::locale; - - $main::auth->assert('general_ledger | vendor_invoice_edit | sales_order_edit | invoice_edit |' . - 'request_quotation_edit | sales_quotation_edit | purchase_order_edit | cash | report'); - - my $nextsub = shift || 'update'; - - for my $i (1 .. $form->{rowcount}) { - my $suffix = $i ? "_$i" : ""; - my $prefix = $i ? "" : "global"; - $form->{"${prefix}project_id${suffix}"} = "" unless $form->{"${prefix}projectnumber$suffix"}; - if ($form->{"${prefix}projectnumber${suffix}"} ne $form->{"old${prefix}projectnumber${suffix}"}) { - if ($form->{"${prefix}projectnumber${suffix}"}) { - - # get new project - $form->{projectnumber} = $form->{"${prefix}projectnumber${suffix}"}; - my %params = map { $_ => $form->{$_} } qw(projectnumber description active); - my $rows; - if (($rows = Projects->search_projects(%params)) > 1) { - - # check form->{project_list} how many there are - $form->{rownumber} = $i; - &select_project($i ? undef : 1, $nextsub); - ::end_of_request(); - } - - if ($rows == 1) { - $form->{"${prefix}project_id${suffix}"} = $form->{project_list}->[0]->{id}; - $form->{"${prefix}projectnumber${suffix}"} = $form->{project_list}->[0]->{projectnumber}; - $form->{"old${prefix}projectnumber${suffix}"} = $form->{project_list}->[0]->{projectnumber}; - } else { - - # not on file - $form->error($locale->text('Project not on file!')); - } - } else { - $form->{"old${prefix}projectnumber${suffix}"} = ""; - } - } - } - - $main::lxdebug->leave_sub(); -} - sub select_project { $::lxdebug->enter_sub; @@ -420,7 +370,6 @@ arap.pl - helper functions or customer/vendor retrieval =head1 SYNOPSIS check_name('vendor') - check_project(); =head1 DESCRIPTION diff --git a/bin/mozilla/projects.pl b/bin/mozilla/projects.pl deleted file mode 100644 index c1b8b7d6e..000000000 --- a/bin/mozilla/projects.pl +++ /dev/null @@ -1,316 +0,0 @@ -#===================================================================== -# LX-Office ERP -# Copyright (C) 2004 -# Based on SQL-Ledger Version 2.1.9 -# Web http://www.lx-office.org -# -#===================================================================== -# SQL-Ledger Accounting -# Copyright (c) 1998-2002 -# -# Author: Dieter Simader -# Email: dsimader@sql-ledger.org -# Web: http://www.sql-ledger.org -# -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -#====================================================================== -# -# project administration -#====================================================================== - -use POSIX qw(strftime); - -use SL::CVar; -use SL::Projects; -use SL::ReportGenerator; - -require "bin/mozilla/common.pl"; -require "bin/mozilla/reportgenerator.pl"; - -use strict; - -sub add { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my $locale = $main::locale; - - # construct callback - $form->{callback} = build_std_url('action') unless $form->{callback}; - - display_project_form(); - - $main::lxdebug->leave_sub(); -} - -sub edit { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my $locale = $main::locale; - - # show history button - $form->{javascript} = qq||; - #/show hhistory button - $form->{title} = "Edit"; - - $form->{project} = Projects->get_project('id' => $form->{id}, 'orphaned' => 1); - - display_project_form(); - - $main::lxdebug->leave_sub(); -} - -sub search { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my $locale = $main::locale; - - $form->{title} = $locale->text('Projects'); - - $form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'Projects'); - ($form->{CUSTOM_VARIABLES_FILTER_CODE}, - $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES}, - 'include_prefix' => 'l_', - 'include_value' => 'Y'); - $::request->{layout}->focus('#projectnumber'); - - $form->header(); - print $form->parse_html_template('projects/search'); - - $main::lxdebug->leave_sub(); -} - -sub project_report { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my %myconfig = %main::myconfig; - my $locale = $main::locale; - - $form->{sort} ||= 'projectnumber'; - my $filter = $form->{filter} || { }; - - Projects->search_projects(%{ $filter }, 'sort' => $form->{sort}); - - my $cvar_configs = CVar->get_configs('module' => 'Projects'); - - my $report = SL::ReportGenerator->new(\%myconfig, $form); - - my @columns = qw(projectnumber description customer type active valid); - - my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs }; - my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs }; - my %column_defs_cvars = (); - foreach (@includeable_custom_variables) { - $column_defs_cvars{"cvar_$_->{name}"} = { - 'text' => $_->{description}, - 'visible' => $form->{"l_cvar_$_->{name}"} eq 'Y', - }; - } - - push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables; - - - my @hidden_vars = ( - 'filter', - map({ ('cvar_'. $_->{name} , 'l_cvar_'. $_->{name}) } @includeable_custom_variables), - map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)), - ); - my $href = build_std_url('action=project_report', @hidden_vars); - - - my %column_defs = ( - 'projectnumber' => { 'text' => $locale->text('Number'), }, - 'description' => { 'text' => $locale->text('Description'), }, - 'customer' => { 'text' => $locale->text('Customer'), }, - 'type' => { 'text' => $locale->text('Type'), }, - 'active' => { 'text' => $locale->text('Active'), 'visible' => 'both' eq $filter->{active}, }, - 'valid' => { 'text' => $locale->text('Valid'), 'visible' => 'both' eq $filter->{active}, }, - %column_defs_cvars, - ); - - foreach (qw(projectnumber description customer type)) { - $column_defs{$_}->{link} = $href . "&sort=$_"; - $column_defs{$_}->{visible} = 1; - } - - $report->set_columns(%column_defs); - $report->set_column_order(@columns); - - $report->set_export_options('project_report', @hidden_vars, 'sort'); - - CVar->add_custom_variables_to_report('module' => 'Project', - 'trans_id_field' => 'id', - 'configs' => $cvar_configs, - 'column_defs' => \%column_defs, - 'data' => $form->{project_list}, - ); - - $report->set_sort_indicator($form->{sort}, 1); - - my @options; - push @options, $locale->text('All') if ($filter->{all}); - push @options, $locale->text('Orphaned') if ($filter->{orphaned}); - push @options, $locale->text('Project Number') . " : $filter->{projectnumber}" if ($filter->{projectnumber}); - push @options, $locale->text('Description') . " : $filter->{description}" if ($filter->{description}); - push @options, $locale->text('Customer') . " : $filter->{customer}" if ($filter->{customer}); - push @options, $locale->text('Type') . " : $filter->{type}" if ($filter->{type}); - push @options, $locale->text('Active') if ($filter->{active} eq 'active'); - push @options, $locale->text('Inactive') if ($filter->{active} eq 'inactive'); - push @options, $locale->text('Orphaned') if ($filter->{status} eq 'orphaned'); - - $form->{title} = $locale->text('Projects'); - - $report->set_options('top_info_text' => join("\n", @options), - 'output_format' => 'HTML', - 'title' => $form->{title}, - 'attachment_basename' => $locale->text('project_list') . strftime('_%Y%m%d', localtime time), - ); - $report->set_options_from_form(); - $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv'; - - CVar->add_custom_variables_to_report('module' => 'Projects', - 'trans_id_field' => 'id', - 'configs' => $cvar_configs, - 'column_defs' => \%column_defs, - 'data' => $form->{project_list}); - - my $edit_url = build_std_url('action=edit&type=project'); - my $callback = $form->escape($href) . '&sort=' . E($form->{sort}); - - foreach my $project (@{ $form->{project_list} }) { - $project->{active} = $project->{active} ? $locale->text('Yes') : $locale->text('No'); - $project->{valid} = $project->{valid} ? $locale->text('Yes') : $locale->text('No'); - - my $row = { map { $_ => { 'data' => $project->{$_} } } keys %{ $project } }; - - $row->{projectnumber}->{link} = $edit_url . "&id=" . E($project->{id}) . "&callback=${callback}"; - - $report->add_data($row); - } - - $report->generate_with_headers(); - - $main::lxdebug->leave_sub(); -} - -sub display_project_form { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my $locale = $main::locale; - - $form->{project} ||= { }; - - $form->{title} = $form->{project}->{id} ? $locale->text("Edit Project") : $locale->text("Add Project"); - - $form->{ALL_CUSTOMERS} = SL::DB::Manager::Customer->get_all_sorted(where => [ or => [ obsolete => 0, obsolete => undef, id => $form->{project}->{customer_id} ]]); - $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'Projects', 'trans_id' => $form->{project}->{id}); -# $main::lxdebug->dump(0, "cv", $form->{CUSTOM_VARIABLES}); - CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}) if (scalar @{ $form->{CUSTOM_VARIABLES} }); - - $form->header(); - print $form->parse_html_template('projects/project_form'); - - $main::lxdebug->leave_sub(); -} - -sub save { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my %myconfig = %main::myconfig; - my $locale = $main::locale; - - $form->isblank("project.projectnumber", $locale->text('Project Number missing!')); - - my $project = $form->{project} || { }; - my $is_new = !$project->{id}; - $project->{id} = Projects->save_project(%{ $project }); - - # saving the history - if(!exists $form->{addition} && $project->{id} ne "") { - $form->{id} = $project->{id}; - $form->{snumbers} = qq|projectnumber_| . $project->{projectnumber}; - $form->{addition} = "SAVED"; - $form->save_history; - } - # /saving the history - - if ($form->{callback}) { - map { $form->{callback} .= "&new_${_}=" . $form->escape($project->{$_}); } qw(projectnumber description id); - my $message = $is_new ? $locale->text('The project has been added.') : $locale->text('The project has been saved.'); - $form->{callback} .= "&message=" . E($message); - } - - $form->redirect($locale->text('Project saved!')); - - $main::lxdebug->leave_sub(); -} - -sub save_as_new { - $main::lxdebug->enter_sub(); - - my $form = $main::form; - my $locale = $main::locale; - - delete $form->{project}->{id} if ($form->{project}); - save(); - - $main::lxdebug->leave_sub(); -} - -sub delete { - $main::lxdebug->enter_sub(); - - $main::auth->assert('project_edit'); - - my $form = $main::form; - my %myconfig = %main::myconfig; - my $locale = $main::locale; - - my $project = $form->{project} || { }; - Projects->delete_project('id' => $project->{id}); - - # saving the history - if(!exists $form->{addition}) { - $form->{snumbers} = qq|projectnumber_| . $project->{projectnumber}; - $form->{addition} = "DELETED"; - $form->save_history; - } - # /saving the history - - $form->redirect($locale->text('Project deleted!')); - - $main::lxdebug->leave_sub(); -} - -sub continue { - call_sub($main::form->{nextsub}); -} diff --git a/bin/mozilla/rp.pl b/bin/mozilla/rp.pl index bc2eab6e2..9f6d848fd 100644 --- a/bin/mozilla/rp.pl +++ b/bin/mozilla/rp.pl @@ -37,11 +37,13 @@ use POSIX qw(strftime); +use SL::DB::Project; use SL::PE; use SL::RP; use SL::Iconv; use SL::ReportGenerator; use Data::Dumper; +use List::MoreUtils qw(any); require "bin/mozilla/arap.pl"; require "bin/mozilla/common.pl"; @@ -213,35 +215,6 @@ sub report { sub continue { call_sub($main::form->{"nextsub"}); } -sub get_project { - $main::lxdebug->enter_sub(); - - $main::auth->assert('report'); - - my $form = $main::form; - my %myconfig = %main::myconfig; - my $locale = $main::locale; - - my $nextsub = shift; - - $form->{project_id} = $form->{project_id_1}; - if ($form->{projectnumber} && !$form->{project_id}) { - $form->{rowcount} = 1; - - # call this instead of update - $form->{update} = $nextsub; - $form->{projectnumber_1} = $form->{projectnumber}; - - delete $form->{sort}; - check_project('generate_projects'); - - # if there is one only, assign id - $form->{project_id} = $form->{project_id_1}; - } - - $main::lxdebug->leave_sub(); -} - sub generate_income_statement { $main::lxdebug->enter_sub(); @@ -460,8 +433,8 @@ sub generate_projects { my %myconfig = %main::myconfig; my $locale = $main::locale; - &get_project("generate_projects"); - $form->{projectnumber} = $form->{projectnumber_1}; + my $project = $form->{project_id} ? SL::DB::Project->new(id => $form->{project_id})->load : undef; + $form->{projectnumber} = $project ? $project->projectnumber : ''; $form->{nextsub} = "generate_projects"; $form->{title} = $locale->text('Project Transactions'); diff --git a/locale/de/all b/locale/de/all index ebafe70ef..61e797704 100644 --- a/locale/de/all +++ b/locale/de/all @@ -468,6 +468,7 @@ $self->{texts} = { 'Create a new business' => 'Einen neuen Kunden-/Lieferantentyp erfassen', 'Create a new department' => 'Eine neue Abteilung erfassen', 'Create a new payment term' => 'Neue Zahlungsbedingungen anlegen', + 'Create a new project' => 'Neues Projekt anlegen', 'Create a standard group' => 'Eine Standard-Benutzergruppe anlegen', 'Create and edit RFQs' => 'Lieferantenanfragen erfassen und bearbeiten', 'Create and edit dunnings' => 'Mahnungen erfassen und bearbeiten', @@ -741,7 +742,6 @@ $self->{texts} = { 'Edit Price Factor' => 'Preisfaktor bearbeiten', 'Edit Pricegroup' => 'Preisgruppe bearbeiten', 'Edit Printer' => 'Drucker bearbeiten', - 'Edit Project' => 'Projekt bearbeiten', 'Edit Purchase Delivery Order' => 'Lieferschein (Einkauf) bearbeiten', 'Edit Purchase Order' => 'Lieferantenaufrag bearbeiten', 'Edit Quotation' => 'Angebot bearbeiten', @@ -772,6 +772,8 @@ $self->{texts} = { 'Edit note' => 'Notiz bearbeiten', 'Edit payment term' => 'Zahlungsbedingungen bearbeiten', 'Edit prices and discount (if not used, textfield is ONLY set readonly)' => 'Preise und Rabatt in Formularen frei anpassen (falls deaktiviert, wird allerdings NUR das textfield auf READONLY gesetzt / kann je nach Browserversion und technischen Fähigkeiten des Anwenders noch umgangen werden)', + 'Edit project' => 'Projekt bearbeiten', + 'Edit project #1' => 'Projekt #1 bearbeiten', 'Edit rights' => 'Rechte bearbeiten', 'Edit templates' => 'Vorlagen bearbeiten', 'Edit the Delivery Order' => 'Lieferschein bearbeiten', @@ -1490,12 +1492,8 @@ $self->{texts} = { 'Project' => 'Projekt', 'Project Description' => 'Projektbeschreibung', 'Project Number' => 'Projektnummer', - 'Project Number missing!' => 'Projektnummer fehlt!', 'Project Numbers' => 'Projektnummern', 'Project Transactions' => 'Projektbuchungen', - 'Project deleted!' => 'Projekt gelöscht!', - 'Project not on file!' => 'Dieses Projekt ist nicht in der Datenbank!', - 'Project saved!' => 'Projekt gespeichert!', 'Projects' => 'Projekte', 'Projecttransactions' => 'Projektbuchungen', 'Prozentual/Absolut' => 'Prozentual/Absolut', @@ -1655,6 +1653,7 @@ $self->{texts} = { 'Search AP Aging' => 'Offene Verbindlichkeiten', 'Search AR Aging' => 'Offene Forderungen', 'Search contacts' => 'Ansprechpersonensuche', + 'Search projects' => 'Projektsuche', 'Search term' => 'Suchbegriff', 'Searchable' => 'Durchsuchbar', 'Secondary sorting' => 'Untersortierung', @@ -1990,8 +1989,12 @@ $self->{texts} = { 'The profile \'#1\' has been deleted.' => 'Das Profil \'#1\' wurde gelöscht.', 'The profile has been saved under the name \'#1\'.' => 'Das Profil wurde unter dem Namen \'#1\' gespeichert.', 'The program\'s exit code was #1 ("0" usually means that everything went OK).' => 'Der Exitcode des Programms war #1 ("0" bedeutet normalerweise, dass die Wiederherstellung erfolgreich war).', - 'The project has been added.' => 'Das Projekt wurde erfasst.', + 'The project has been created.' => 'Das Projekt wurde angelegt.', + 'The project has been deleted.' => 'Das Projekt wurde gelöscht.', 'The project has been saved.' => 'Das Projekt wurde gespeichert.', + 'The project is in use and cannot be deleted.' => 'Das Projekt ist in Verwendung und kann nicht gelöscht werden.', + 'The project number is already in use.' => 'Die Projektnummer wird bereits verwendet.', + 'The project number is missing.' => 'Die Projektnummer fehlt.', 'The restoration process has started. Here\'s the output of the "pg_restore" command:' => 'Der Wiederherstellungsprozess wurde gestartet. Hier ist die Ausgabe des "pg_restore"-Programmes:', 'The restoration process is complete. Please review "pg_restore"\'s output to find out if the restoration was successful.' => 'Die Wiederherstellung ist abgeschlossen. Bitte sehen Sie sich die Ausgabe von "pg_restore" an, um festzustellen, ob die Wiederherstellung erfolgreich war.', 'The second reason is that kivitendo allowed the user to enter the tax amount manually regardless of the taxkey used.' => 'Der zweite Grund war, dass kivitendo zuließ, dass die Benutzer beliebige, von den tatsächlichen Steuerschlüsseln unabhängige Steuerbeträge eintrugen.', @@ -2436,7 +2439,6 @@ $self->{texts} = { 'prev' => 'zurück', 'print' => 'drucken', 'proforma' => 'Proforma', - 'project_list' => 'projektliste', 'purchase_delivery_order_list' => 'lieferscheinliste_einkauf', 'purchase_order' => 'Auftrag', 'purchase_order_list' => 'lieferantenauftragsliste', diff --git a/menu.ini b/menu.ini index 65a97c96c..5f85eed4c 100644 --- a/menu.ini +++ b/menu.ini @@ -32,8 +32,8 @@ item=assembly [Master Data--Add Project] ACCESS=project_edit -module=projects.pl -action=add +module=controller.pl +action=Project/new [Master Data--Update Prices] ACCESS=part_service_assembly_edit @@ -84,8 +84,8 @@ searchitems=assembly [Master Data--Reports--Projects] ACCESS=project_edit -module=projects.pl -action=search +module=controller.pl +action=Project/search [AR] diff --git a/projects.pl b/projects.pl deleted file mode 120000 index 385000d1b..000000000 --- a/projects.pl +++ /dev/null @@ -1 +0,0 @@ -am.pl \ No newline at end of file diff --git a/templates/webpages/projects/project_form.html b/templates/webpages/project/form.html similarity index 53% rename from templates/webpages/projects/project_form.html rename to templates/webpages/project/form.html index 3b6f7f45a..2ea52bc8f 100644 --- a/templates/webpages/projects/project_form.html +++ b/templates/webpages/project/form.html @@ -2,18 +2,13 @@ [%- USE L %] [%- USE HTML %][%- USE LxERP %] - [%- IF message %] -

[% message %]

+[%- INCLUDE 'common/flash.html' %] -
- [%- END %] +
[% title %]
-

[% title %] [% HTML.escape(project.projectnumber) %]

- -
- - - + + [% L.hidden_tag("callback", callback) %] + [% L.hidden_tag("id", SELF.project.id) %]
  • [% 'Basic Data' | $T8 %]
  • @@ -29,40 +24,40 @@ - + - + - + - + - [%- IF project.id %] + [%- IF SELF.project.id %] - + [%- END %]
    [% 'Number' | $T8 %][% L.input_tag("project.projectnumber", SELF.project.projectnumber, size=60) %]
    [% 'Description' | $T8 %] - [%- SET rows = LxERP.numtextrows(project.description, 60) %] + [%- SET rows = LxERP.numtextrows(SELF.project.description, 60) %] [%- IF rows > 1 %] - + [%- L.textarea_tag("project.description", SELF.project.description, rows=row, size=60, style="width: 100%", wrap="soft") %] [%- ELSE %] - + [%- L.input_tag("project.description", SELF.project.description, size=60) %] [%- END %]
    [% 'Type' | $T8 %][% L.input_tag('project.type', project.type, size=60) %][% L.input_tag('project.type', SELF.project.type, size=60) %]
    [% 'Customer' | $T8 %][% L.select_tag('project.customer_id', ALL_CUSTOMERS, default=project.customer_id, title_key='name', style='width: 300px') %][% L.select_tag('project.customer_id', ALL_CUSTOMERS, default=SELF.project.customer_id, title_key='name', style='width: 300px') %]
    [% 'Valid' | $T8 %][% L.select_tag('project.valid', [ [ 1, LxERP.t8('Valid') ], [ 0, LxERP.t8('Invalid') ] ], default=project.valid, style='width: 300px') %][% L.select_tag('project.valid', [ [ 1, LxERP.t8('Valid') ], [ 0, LxERP.t8('Invalid') ] ], default=SELF.project.valid, style='width: 300px') %]
    [% 'Active' | $T8 %][% L.select_tag('project.active', [ [ 1, LxERP.t8('Active') ], [ 0, LxERP.t8('Inactive') ] ], default=project.valid, style='width: 300px') %][% L.select_tag('project.active', [ [ 1, LxERP.t8('Active') ], [ 0, LxERP.t8('Inactive') ] ], default=SELF.project.active, style='width: 300px') %]
    @@ -91,15 +86,14 @@

    - [% L.online_help_tag('add_project') %] - - [%- IF project.id %] - - [%- IF project.orphaned %] - - [%- END %] - + [% L.online_help_tag('add_project') %] + [% L.hidden_tag("action", "Project/dispatch") %] + [% L.submit_tag("action_" _ (SELF.project.id ? "update" : "create"), LxERP.t8('Save')) %] + [%- IF SELF.project.id %] + [% L.submit_tag("action_create", LxERP.t8('Save as new')) %] + [% L.submit_tag("action_destroy", LxERP.t8('Delete'), confirm=LxERP.t8('Do you really want to delete this object?')) IF !SELF.project.is_used %] [%- END %] + [%- LxERP.t8('Abort') %]

    diff --git a/templates/webpages/project/report_bottom.html b/templates/webpages/project/report_bottom.html new file mode 100644 index 000000000..79e152304 --- /dev/null +++ b/templates/webpages/project/report_bottom.html @@ -0,0 +1,2 @@ +[% USE L %] +[%- L.paginate_controls %] diff --git a/templates/webpages/project/search.html b/templates/webpages/project/search.html new file mode 100644 index 000000000..e5d4ff4ac --- /dev/null +++ b/templates/webpages/project/search.html @@ -0,0 +1,65 @@ +[%- USE T8 %] +[%- USE HTML %] +[%- USE L %] +[%- USE LxERP %] + +[%- INCLUDE 'common/flash.html' %] + +
    + +
    [% 'Search projects' | $T8 %]
    + +

    + + + + + + + + + + + + + + + + + + + + + + [% CUSTOM_VARIABLES_FILTER_CODE %] + + + + + +
    [% 'Number' | $T8 %][% L.input_tag('filter.projectnumber:substr::ilike', filter.projectnumber_substr__ilike, size=60) %]
    [% 'Description' | $T8 %][% L.input_tag('filter.description:substr::ilike', filter.description_substr__ilike, size=60) %]
    [% 'Customer' | $T8 %][% L.input_tag('filter.customer.name:substr::ilike', filter.customer.name_substr__ilike, size=60) %]
    [% 'Type' | $T8 %][% L.input_tag('filter.type:substr::ilike', filter.type_substr__ilike, size=20) %]
    [% 'Include in Report' | $T8 %] + + + + + + + + + + + + + + [% CUSTOM_VARIABLES_INCLUSION_CODE %] + +
    [% L.select_tag('filter.active', [ [ 'active', LxERP.t8('Active') ], [ 'inactive', LxERP.t8('Inactive') ], [ 'both', LxERP.t8('Both') ] ], default=filter.active, style="width: 200px") %]
    [% L.select_tag('filter.valid', [ [ 'valid', LxERP.t8('Valid') ], [ 'invalid', LxERP.t8('Invalid') ], [ 'both', LxERP.t8('Both') ] ], default=filter.valid, style="width: 200px") %]
    [% L.select_tag('filter.status', [ [ 'all', LxERP.t8('All') ], [ 'orphaned', LxERP.t8('Orphaned') ] ], default=filter.status, style="width: 200px") %]
    +
    +

    + +
    + + [% L.hidden_tag('action', 'Project/list') %] + +

    [% L.submit_tag('dummy', LxERP.t8('Continue')) %]

    +
    diff --git a/templates/webpages/projects/search.html b/templates/webpages/projects/search.html deleted file mode 100644 index 5eafb4024..000000000 --- a/templates/webpages/projects/search.html +++ /dev/null @@ -1,91 +0,0 @@ -[%- USE T8 %] -[%- USE HTML %] -
    - -
    [% title %]
    - -

    - - - - - - - - - - - - - - - - - - - - - - [% CUSTOM_VARIABLES_FILTER_CODE %] - - - - - -
    [% 'Number' | $T8 %]
    [% 'Description' | $T8 %]
    [% 'Customer' | $T8 %]
    [% 'Type' | $T8 %]
    [% 'Include in Report' | $T8 %] - - - - - - - - - - - - - - - - - - - [% CUSTOM_VARIABLES_INCLUSION_CODE %] - -
    - - - - - - - - -
    - - - - - - - - -
    - - - - - -
    -
    -

    - -
    - - - -

    - -

    -
    diff --git a/templates/webpages/rp/report.html b/templates/webpages/rp/report.html index c4e8692c3..afffd5c0c 100644 --- a/templates/webpages/rp/report.html +++ b/templates/webpages/rp/report.html @@ -89,10 +89,7 @@ [%- END %] [%- IF is_projects %] - - [% 'Project' | $T8 %] - - + [% INCLUDE projectnumber %] [% 'as at' | $T8 %] @@ -364,4 +361,3 @@ - -- 2.20.1