Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision b2e1809f

Von Moritz Bunkus vor fast 11 Jahren hinzugefügt

  • ID b2e1809f6b6d8852ab0086dcc61ae9540c18b83f
  • Vorgänger d157b20f
  • Nachfolger 7a21a080

Pflichtenhefte: Basisdaten verwalten, Such- und Listfunktion

Unterschiede anzeigen:

SL/Controller/RequirementSpec.pm
package SL::Controller::RequirementSpec;
use strict;
use parent qw(SL::Controller::Base);
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::DB::Customer;
use SL::DB::Project;
use SL::DB::RequirementSpecStatus;
use SL::DB::RequirementSpecType;
use SL::DB::RequirementSpec;
use SL::Helper::Flash;
use SL::Locale::String;
use Rose::Object::MakeMethods::Generic
(
scalar => [ qw(requirement_spec customers projects types statuses db_args flat_filter is_template) ],
);
__PACKAGE__->run_before('setup');
__PACKAGE__->run_before('load_requirement_spec', only => [ qw( edit update destroy) ]);
__PACKAGE__->run_before('load_select_options', only => [ qw(new edit create update list) ]);
__PACKAGE__->run_before('load_search_select_options', only => [ qw( list) ]);
__PACKAGE__->get_models_url_params('flat_filter');
__PACKAGE__->make_paginated(
MODEL => 'RequirementSpec',
PAGINATE_ARGS => 'db_args',
ONLY => [ qw(list) ],
);
__PACKAGE__->make_sorted(
MODEL => 'RequirementSpec',
ONLY => [ qw(list) ],
DEFAULT_BY => 'customer',
DEFAULT_DIR => 1,
customer => t8('Customer'),
title => t8('Title'),
type => t8('Requirement Spec Type'),
status => t8('Requirement Spec Status'),
projectnumber => t8('Project Number'),
);
#
# actions
#
sub action_list {
my ($self) = @_;
$self->setup_db_args_from_filter;
$self->flat_filter({ map { $_->{key} => $_->{value} } $::form->flatten_variables('filter') });
$self->prepare_report;
my $requirement_specs = $self->get_models(%{ $self->db_args });
$self->report_generator_list_objects(report => $self->{report}, objects => $requirement_specs);
}
sub action_new {
my ($self) = @_;
$self->{requirement_spec} = SL::DB::RequirementSpec->new;
$self->render('requirement_spec/form', title => t8('Create a new requirement spec'));
}
sub action_edit {
my ($self) = @_;
$self->render('requirement_spec/form', title => t8('Edit requirement spec'));
}
sub action_create {
my ($self) = @_;
$self->{requirement_spec} = SL::DB::RequirementSpec->new;
$self->create_or_update;
}
sub action_update {
my ($self) = @_;
$self->create_or_update;
}
sub action_destroy {
my ($self) = @_;
if (eval { $self->{requirement_spec}->delete; 1; }) {
flash_later('info', t8('The requirement spec has been deleted.'));
} else {
flash_later('error', t8('The requirement spec is in use and cannot be deleted.'));
}
$self->redirect_to(action => 'list');
}
sub action_reorder {
my ($self) = @_;
SL::DB::RequirementSpec->reorder_list(@{ $::form->{requirement_spec_id} || [] });
$self->render('1;', { type => 'js', inline => 1 });
}
#
# filters
#
sub setup {
my ($self) = @_;
$::auth->assert('config');
$::request->{layout}->use_stylesheet("requirement_spec.css");
$self->is_template($::form->{is_template} ? 1 : 0);
return 1;
}
#
# helpers
#
sub create_or_update {
my $self = shift;
my $is_new = !$self->{requirement_spec}->id;
my $params = delete($::form->{requirement_spec}) || { };
my $title = $is_new ? t8('Create a new requirement spec') : t8('Edit requirement spec');
$self->{requirement_spec}->assign_attributes(%{ $params });
my @errors = $self->{requirement_spec}->validate;
if (@errors) {
flash('error', @errors);
$self->render('requirement_spec/form', title => $title);
return;
}
$self->{requirement_spec}->save;
flash_later('info', $is_new ? t8('The requirement spec has been created.') : t8('The requirement spec has been saved.'));
$self->redirect_to(action => 'list');
}
sub load_requirement_spec {
my ($self) = @_;
$self->{requirement_spec} = SL::DB::RequirementSpec->new(id => $::form->{id})->load;
}
sub load_select_options {
my ($self) = @_;
my @filter = ('!obsolete' => 1);
if ($self->requirement_spec && $self->requirement_spec->customer_id) {
@filter = ( or => [ @filter, id => $self->requirement_spec->customer_id ] );
}
$self->customers(SL::DB::Manager::Customer->get_all_sorted(where => \@filter));
$self->statuses( SL::DB::Manager::RequirementSpecStatus->get_all_sorted);
$self->types( SL::DB::Manager::RequirementSpecType->get_all_sorted);
}
sub load_search_select_options {
my ($self) = @_;
$self->projects(SL::DB::Manager::Project->get_all_sorted);
}
sub setup_db_args_from_filter {
my ($self) = @_;
$self->{filter} = {};
my %args = parse_filter(
$::form->{filter},
with_objects => [ 'customer', 'type', 'status', 'project' ],
launder_to => $self->{filter},
);
$args{where} = [
and => [
@{ $args{where} || [] },
is_template => $self->is_template
]];
$self->db_args(\%args);
}
sub prepare_report {
my ($self) = @_;
my $callback = $self->get_callback;
my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
$self->{report} = $report;
my @columns = qw(title customer status type projectnumber);
my @sortable = qw(title customer status type projectnumber);
my %column_defs = (
title => { obj_link => sub { $self->url_for(action => 'edit', id => $_[0]->id, callback => $callback) } },
customer => { raw_data => sub { $self->presenter->customer($_[0]->customer, display => 'table-cell', callback => $callback) },
sub => sub { $_[0]->customer->name } },
projectnumber => { raw_data => sub { $self->presenter->project($_[0]->project, display => 'table-cell', callback => $callback) },
sub => sub { $_[0]->project_id ? $_[0]->project->projectnumber : '' } },
status => { sub => sub { $_[0]->status->description } },
type => { sub => sub { $_[0]->type->description } },
);
map { $column_defs{$_}->{text} ||= $::locale->text( $self->get_sort_spec->{$_}->{title} ) } keys %column_defs;
$report->set_options(
std_column_visibility => 1,
controller_class => 'RequirementSpec',
output_format => 'HTML',
raw_top_info_text => $self->render('requirement_spec/report_top', { output => 0 }),
raw_bottom_info_text => $self->render('requirement_spec/report_bottom', { output => 0 }),
title => $::locale->text('Requirement Specs'),
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;
}
1;
SL/DB/Manager/RequirementSpec.pm
package SL::DB::Manager::RequirementSpec;
use strict;
use SL::DB::Helper::Manager;
use base qw(SL::DB::Helper::Manager);
use SL::DB::Helper::Paginated;
use SL::DB::Helper::Sorted;
sub object_class { 'SL::DB::RequirementSpec' }
__PACKAGE__->make_manager_methods;
sub _sort_spec {
return (
default => [ 'title', 1 ],
columns => {
SIMPLE => 'ALL',
customer => 'lower(customer.name)',
type => 'type.position',
status => 'status.position',
projectnumber => 'project.projectnumber',
map { ( $_ => "lower(requirement_specs.${_})" ) } qw(title),
});
}
1;
SL/DB/Project.pm
return !SL::DB::Manager::Project->get_first(where => \@filter);
}
sub full_description {
my ($self, %params) = @_;
$params{style} ||= 'both';
my $description;
if ($params{style} =~ m/number/) {
$description = $self->projectnumber;
} elsif ($params{style} =~ m/description/) {
$description = $self->description;
} else {
$description = $self->projectnumber;
if ($self->description && do { my $desc = quotemeta $self->description; $self->projectnumber !~ m/$desc/ }) {
$description .= ' (' . $self->description . ')';
}
}
return $description;
}
1;
__END__
......
project in the database. Also returns trueish if no project number has
been set yet.
=item C<full_description %params>
Returns a full description for the project which can consist of the
project number, its description or both. This is determined by the
parameter C<style> which defaults to C<both>:
=over 2
=item C<both>
Returns the project's number followed by its description in
parenthesis (e.g. "12345 (Secret Combinations)"). If the project's
description is already part of the project's number then it will not
be appended.
=item C<projectnumber> (or simply C<number>)
Returns only the project's number.
=item C<projectdescription> (or simply C<description>)
Returns only the project's description.
=back
=back
=head1 AUTHOR
SL/DB/RequirementSpec.pm
use strict;
use SL::DB::MetaSetup::RequirementSpec;
use SL::DB::Manager::RequirementSpec;
use SL::Locale::String;
__PACKAGE__->meta->add_relationship(
......
},
);
# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
__PACKAGE__->meta->make_manager_class;
__PACKAGE__->meta->initialize;
__PACKAGE__->before_save('_before_save_initialize_not_null_columns');
sub validate {
my ($self) = @_;
......
return @errors;
}
sub _before_save_initialize_not_null_columns {
my ($self) = @_;
$self->previous_section_number(0) if !defined $self->previous_section_number;
$self->previous_fb_number(0) if !defined $self->previous_fb_number;
return 1;
}
1;
SL/Presenter/CustomerVendor.pm
croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
my $callback = $params{callback} ? '&callback=' . $::form->escape($params{callback}) : '';
my $text = join '', (
$params{no_link} ? '' : '<a href="controller.pl?action=CustomerVendor/edit&amp;db=' . $type . '&amp;id=' . $self->escape($cv->id) . '">',
$self->escape($cv->name),
SL/Presenter/Project.pm
croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
$params{style} ||= 'both';
my $description;
if ($params{style} =~ m/number/) {
$description = $project->projectnumber;
} elsif ($params{style} =~ m/description/) {
$description = $project->description;
} else {
$description = $project->projectnumber;
if ($project->description && do { my $desc = quotemeta $project->description; $project->projectnumber !~ m/$desc/ }) {
$description .= ' (' . $project->description . ')';
}
}
my $description = $project->full_description(style => $params{style});
my $callback = $params{callback} ? '&callback=' . $::form->escape($params{callback}) : '';
my $text = join '', (
$params{no_link} ? '' : '<a href="controller.pl?action=Project/edit&amp;id=' . $self->escape($project->id) . '">',
$params{no_link} ? '' : '<a href="controller.pl?action=Project/edit&amp;id=' . $self->escape($project->id) . $callback . '">',
$self->escape($description),
$params{no_link} ? '' : '</a>',
);
css/requirement_spec.css
input.rs_input_field, select.rs_input_field,
table.rs_input_field input, table.rs_input_field select {
width: 300px;
}
locale/de/all
'Add Quotation' => 'Angebot erfassen',
'Add RFQ' => 'Preisanfrage erfassen',
'Add Request for Quotation' => 'Anfrage erfassen',
'Add Requirement Spec' => 'Neues Pflichtenheft erfassen',
'Add Sales Delivery Order' => 'Lieferschein (Verkauf) erfassen',
'Add Sales Invoice' => 'Rechnung erfassen',
'Add Sales Order' => 'Auftrag erfassen',
......
'Create a new predefined text' => 'Einen neuen vordefinierten Textblock anlegen',
'Create a new project' => 'Neues Projekt anlegen',
'Create a new project type' => 'Einen neuen Projekttypen anlegen',
'Create a new requirement spec' => 'Ein neues Pflichtenheft anlegen',
'Create a new requirement spec status' => 'Einen neuen Pflichtenheftstatus anlegen',
'Create a new requirement spec type' => 'Einen neuen Pflichtenhefttypen anlegen',
'Create a new risk level' => 'Einen neuen Risikograd anlegen',
......
'Edit project' => 'Projekt bearbeiten',
'Edit project #1' => 'Projekt #1 bearbeiten',
'Edit project type' => 'Projekttypen bearbeiten',
'Edit requirement spec' => 'Pflichtenheft bearbeiten',
'Edit requirement spec status' => 'Pflichtenheftstatus bearbeiten',
'Edit requirement spec type' => 'Pflichtenhefttypen bearbeiten',
'Edit risk level' => 'Risikograd bearbeiten',
......
'History Search Engine' => 'Historien Suchmaschine',
'Homepage' => 'Homepage',
'Host' => 'Datenbankcomputer',
'Hourly Rate' => 'Stundensatz',
'Hourly rate' => 'Stundensatz',
'However, you can create a new part which will then be selected.' => 'Sie k&ouml;nnen jedoch einen neuen Artikel anlegen, der dann automatisch ausgew&auml;hlt wird.',
'I' => 'I',
......
'Requested execution date to' => 'Gewünschtes Ausführungsdatum bis',
'Requests for Quotation' => 'Preisanfragen',
'Required by' => 'Lieferdatum',
'Requirement Spec Status' => 'Pflichtenheftstatus',
'Requirement Spec Statuses' => 'Pflichtenheftstatus',
'Requirement Spec Type' => 'Pflichtenhefttyp',
'Requirement Spec Types' => 'Pflichtenhefttypen',
'Requirement Specs' => 'Pflichtenhefte',
'Requirement specs' => 'Pflichtenhefte',
'Reset' => 'Zurücksetzen',
'Result' => 'Ergebnis',
......
'The project type is in use and cannot be deleted.' => 'Der Projekttyp wird verwendet und kann nicht gelöscht werden.',
'The required information consists of the IBAN and the BIC.' => 'Die benötigten Informationen bestehen aus der IBAN und der BIC.',
'The required information consists of the IBAN, the BIC, the mandator ID and the mandate\'s date of signature.' => 'Die benötigten Informationen bestehen aus IBAN, BIC, Mandanten-ID und dem Unterschriftsdatum des Mandates.',
'The requirement spec has been created.' => 'Das Pflichtenheft wurde angelegt.',
'The requirement spec has been deleted.' => 'Das Pflichtenheft wurde gelöscht.',
'The requirement spec has been saved.' => 'Das Pflichtenheft wurde gespeichert.',
'The requirement spec is in use and cannot be deleted.' => 'Das Pflichtenheft wird verwendet und kann nicht gelöscht werden.',
'The requirement spec status has been created.' => 'Der Pflichtenheftstatus wurde angelegt.',
'The requirement spec status has been deleted.' => 'Der Pflichtenheftstatus wurde gelöscht.',
'The requirement spec status has been saved.' => 'Der Pflichtenheftstatus wurde gespeichert.',
menus/erp.ini
module=dn.pl
action=add
[AR--Add Requirement Spec]
module=controller.pl
action=RequirementSpec/new
[AR--Reports]
module=menu.pl
action=acc_menu
......
module=dn.pl
action=search
[AR--Reports--Requirement Specs]
module=controller.pl
action=RequirementSpec/list
[AR--Reports--Delivery Plan]
ACCESS=delivery_plan
module=controller.pl
templates/webpages/requirement_spec/_filter.html
[%- USE HTML %][%- USE L %][%- USE LxERP %]
<div class="filter_toggle">
<a href="#" onClick="javascript:$('.filter_toggle').toggle()">[% LxERP.t8("Show Filter") %]</a>
</div>
<div class="filter_toggle" style="display:none">
<a href="#" onClick="javascript:$('.filter_toggle').toggle()">[% LxERP.t8("Hide Filter") %]</a>
<form method="post" action="controller.pl">
<p>
<table class="rs_input_field">
<tr>
<th align="right">[% LxERP.t8("Title") %]</th>
<td>[% L.input_tag('filter.title:substr::ilike', filter.title_substr__ilike) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Customer") %]</th>
<td>[% L.input_tag('filter.customer.name:substr::ilike', filter.customer.name_substr__ilike) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Customer Number") %]</th>
<td>[% L.input_tag('filter.customer.customernumber:substr::ilike', filter.customer.customernumber_substr__ilike) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Requirement Spec Type") %]</th>
<td>[% L.select_tag('filter.type_id', SELF.types, default=filter.type_id, title_key="description", with_empty=1) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Requirement Spec Status") %]</th>
<td>[% L.select_tag('filter.status_id', SELF.statuses, default=filter.status_id, title_key="description", with_empty=1) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Project") %]</th>
<td>[% L.select_tag('filter.project_id', SELF.projects, default=filter.project_id, title_key="full_description", with_empty=1) %]</td>
</tr>
</table>
</p>
[% L.hidden_tag("action", "RequirementSpec/list") %]
<p>[% L.submit_tag("dummy", LxERP.t8("Continue")) %]</p>
</form>
</div>
templates/webpages/requirement_spec/form.html
[% USE HTML %][% USE L %][% USE LxERP %]
<form method="post" action="controller.pl">
<div class="listtop">[% FORM.title %]</div>
[%- INCLUDE 'common/flash.html' %]
<table class="rs_input_field">
<tr>
<td>[% LxERP.t8("Title") %]</td>
<td>[% L.input_tag("requirement_spec.title", SELF.requirement_spec.description) %]</td>
</tr>
<tr>
<td>[% LxERP.t8("Requirement Spec Type") %]</td>
<td>[% L.select_tag("requirement_spec.type_id", SELF.types, default=SELF.requirement_spec.type_id, title_key="description") %]</td>
</tr>
<tr>
<td>[% LxERP.t8("Requirement Spec Status") %]</td>
<td>[% L.select_tag("requirement_spec.status_id", SELF.statuses, default=SELF.requirement_spec.status_id, title_key="description") %]</td>
</tr>
<tr>
<td>[% LxERP.t8("Customer") %]</td>
<td>[% L.select_tag("requirement_spec.customer_id", SELF.customers, default=SELF.requirement_spec.customer_id, title_key="name", id="customer_id") %]</td>
</tr>
<tr>
<td>[% LxERP.t8("Hourly Rate") %]</td>
<td>[% L.input_tag("requirement_spec.hourly_rate_as_number", SELF.requirement_spec.hourly_rate_as_number, id="hourly_rate") %]</td>
</tr>
</table>
<p>
[% L.hidden_tag("id", SELF.requirement_spec.id) %]
[% L.hidden_tag("action", "RequirementSpec/dispatch") %]
[% L.submit_tag("action_" _ (SELF.requirement_spec.id ? "update" : "create"), LxERP.t8('Save')) %]
[%- IF SELF.requirement_spec.id %]
[% L.submit_tag("action_destroy", LxERP.t8('Delete'), confirm=LxERP.t8('Do you really want to delete this object?')) %]
[%- END %]
<a href="[% SELF.url_for(action="list") %]">[% LxERP.t8('Abort') %]</a>
</p>
</form>
<script type="text/javascript">
<!--
function on_customer_changed() {
$.ajax({
url: 'controller.pl?action=Customer/get_hourly_rate',
dataType: "json",
data: { id: $("#customer_id").attr('value') },
success: function(data) { if (data["hourly_rate"] > 0) $("#hourly_rate").attr("value", data["hourly_rate_formatted"]); }
});
}
$(document).ready(function() {
$("#customer_id").change(on_customer_changed);
});
-->
</script>
templates/webpages/requirement_spec/report_bottom.html
[% USE L %]
[%- L.paginate_controls %]
templates/webpages/requirement_spec/report_top.html
[%- USE L %]
[%- PROCESS "requirement_spec/_filter.html" filter=SELF.filter %]
<hr>

Auch abrufbar als: Unified diff