From: Moritz Bunkus
Date: Fri, 10 May 2013 09:46:39 +0000 (+0200)
Subject: Pflichtenheftabschnittsvorlagen erzeugen, bearbeiten, löschen
X-Git-Tag: release-3.2.0beta~467^2~161
X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=9dffe94b2b63322be723ad557ba1e1222185bf1c;p=kivitendo-erp.git
Pflichtenheftabschnittsvorlagen erzeugen, bearbeiten, löschen
---
diff --git a/SL/Controller/RequirementSpec.pm b/SL/Controller/RequirementSpec.pm
index 804a5af46..abcfbff1e 100644
--- a/SL/Controller/RequirementSpec.pm
+++ b/SL/Controller/RequirementSpec.pm
@@ -25,7 +25,7 @@ use SL::Template::LaTeX;
use Rose::Object::MakeMethods::Generic
(
- scalar => [ qw(requirement_spec_item customers types statuses db_args flat_filter is_template visible_item visible_section) ],
+ scalar => [ qw(requirement_spec_item customers types statuses db_args flat_filter visible_item visible_section) ],
'scalar --get_set_init' => [ qw(requirement_spec complexities risks projects copy_source js) ],
);
@@ -77,13 +77,13 @@ sub action_list {
sub action_new {
my ($self) = @_;
- $self->requirement_spec(SL::DB::RequirementSpec->new);
+ $self->requirement_spec(SL::DB::RequirementSpec->new(is_template => $::form->{is_template}));
if ($self->copy_source) {
$self->requirement_spec->$_($self->copy_source->$_) for qw(type_id status_id customer_id title hourly_rate)
}
- $self->render('requirement_spec/new', title => t8('Create a new requirement spec'));
+ $self->render('requirement_spec/new', title => $self->requirement_spec->is_template ? t8('Create a new requirement spec section template') : t8('Create a new requirement spec'));
}
sub action_ajax_edit {
@@ -220,7 +220,6 @@ sub setup {
$::auth->assert('sales_quotation_edit');
$::request->{layout}->use_stylesheet("${_}.css") for qw(jquery.contextMenu requirement_spec);
$::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree jquery/jquery.contextMenu client_js requirement_spec);
- $self->is_template($::form->{is_template} ? 1 : 0);
$self->init_visible_section;
return 1;
@@ -275,10 +274,14 @@ 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 $title = $is_new && $self->requirement_spec->is_template ? t8('Create a new requirement spec section template')
+ : $is_new ? t8('Create a new requirement spec')
+ : $self->requirement_spec->is_template ? t8('Edit section template')
+ : t8('Edit requirement spec');
+
my @errors = $self->requirement_spec->validate;
if (@errors) {
@@ -306,15 +309,17 @@ sub create_or_update {
return $self->render('requirement_spec/new', title => $title);
}
+ my $info = $self->requirement_spec->is_template ? t8('The section template has been saved.') : t8('The requirement spec has been saved.');
+
if ($::request->is_ajax) {
my $html = $self->render('requirement_spec/_header', { output => 0 });
return $self->invalidate_version
->replaceWith('#requirement-spec-header', $html)
- ->flash('info', t8('The requirement spec has been saved.'))
+ ->flash('info', $info)
->render($self);
}
- flash_later('info', $is_new ? t8('The requirement spec has been created.') : t8('The requirement spec has been saved.'));
+ flash_later('info', $info);
$self->redirect_to(action => 'show', id => $self->requirement_spec->id);
}
@@ -332,7 +337,7 @@ sub setup_db_args_from_filter {
and => [
@{ $args{where} || [] },
working_copy_id => undef,
- is_template => $self->is_template
+ is_template => $::form->{is_template} ? 1 : 0,
]];
$self->db_args(\%args);
@@ -343,33 +348,40 @@ sub prepare_report {
my $callback = $self->get_callback;
+ my $is_template = $::form->{is_template};
my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
$self->{report} = $report;
- my @columns = qw(title customer status type projectnumber mtime version);
- my @sortable = qw(title customer status type projectnumber mtime);
+ my @columns = $is_template ? qw(title mtime) : qw(title customer status type projectnumber mtime version);
+ my @sortable = $is_template ? qw(title mtime) : qw(title customer status type projectnumber mtime);
my %column_defs = (
title => { obj_link => sub { $self->url_for(action => 'show', 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 } },
- version => { sub => sub { $_[0]->version_id ? $_[0]->version->version_number : t8('Working copy without version') } },
mtime => { sub => sub { ($_[0]->mtime || $_[0]->itime)->to_kivitendo(precision => 'minute') } },
);
+ if (!$is_template) {
+ %column_defs = (
+ %column_defs,
+ 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 } },
+ version => { sub => sub { $_[0]->version_id ? $_[0]->version->version_number : t8('Working copy without version') } },
+ );
+ }
+
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_top_info_text => $self->render('requirement_spec/report_top', { output => 0 }, is_template => $is_template),
raw_bottom_info_text => $self->render('requirement_spec/report_bottom', { output => 0 }),
- title => $::locale->text('Requirement Specs'),
+ title => $is_template ? t8('Requirement Spec Section Templates') : t8('Requirement Specs'),
allow_pdf_export => 1,
allow_csv_export => 1,
);
@@ -386,11 +398,12 @@ sub invalidate_version {
my ($self) = @_;
my $rspec = SL::DB::RequirementSpec->new(id => $self->requirement_spec->id)->load;
+ return $self->js if $rspec->is_template;
+
$rspec->invalidate_version;
my $html = $self->render('requirement_spec/_version', { output => 0 }, requirement_spec => $rspec);
return $self->js->html('#requirement_spec_version', $html);
}
-
1;
diff --git a/SL/DB/MetaSetup/RequirementSpec.pm b/SL/DB/MetaSetup/RequirementSpec.pm
index 9971f8662..a6a2c4f04 100644
--- a/SL/DB/MetaSetup/RequirementSpec.pm
+++ b/SL/DB/MetaSetup/RequirementSpec.pm
@@ -11,10 +11,10 @@ __PACKAGE__->meta->setup(
columns => [
id => { type => 'serial', not_null => 1 },
- type_id => { type => 'integer', not_null => 1 },
- status_id => { type => 'integer', not_null => 1 },
+ type_id => { type => 'integer' },
+ status_id => { type => 'integer' },
version_id => { type => 'integer' },
- customer_id => { type => 'integer', not_null => 1 },
+ customer_id => { type => 'integer' },
project_id => { type => 'integer' },
title => { type => 'text', not_null => 1 },
hourly_rate => { type => 'numeric', default => '0', not_null => 1, precision => 2, scale => 8 },
diff --git a/js/locale/de.js b/js/locale/de.js
index 22bd93fe6..bb0c3a904 100644
--- a/js/locale/de.js
+++ b/js/locale/de.js
@@ -7,11 +7,13 @@ namespace("kivi").setupLocale({
"Are you sure?":"Sind Sie sicher?",
"Copy":"Kopieren",
"Copy requirement spec":"Pflichtenheft kopieren",
+"Copy section template":"Abschnittsvorlage kopieren",
"Create PDF":"PDF erzeugen",
"Create new version":"Neue Version anlegen",
"Database Connection Test":"Test der Datenbankverbindung",
"Delete":"Löschen",
"Delete requirement spec":"Pflichtenheft löschen",
+"Delete section template":"Abschnittsvorlage löschen",
"Delete text block":"Textblock löschen",
"Do you really want to cancel?":"Wollen Sie wirklich abbrechen?",
"Do you really want to revert to this version?":"Wollen Sie wirklich auf diese Version zurücksetzen?",
@@ -26,6 +28,7 @@ namespace("kivi").setupLocale({
"Requirement spec actions":"Pflichtenheftaktionen",
"Revert to version":"Auf Version zurücksetzen",
"Save":"Speichern",
+"Section template actions":"Abschnittsvorlagen-Aktionen",
"Section/Function block actions":"Abschnitts-/Funktionsblockaktionen",
"Text block actions":"Textblockaktionen",
"The description is missing.":"Die Beschreibung fehlt.",
diff --git a/js/requirement_spec.js b/js/requirement_spec.js
index d44bf9ff7..e9f9277e8 100644
--- a/js/requirement_spec.js
+++ b/js/requirement_spec.js
@@ -372,46 +372,84 @@ ns.revert_to_versioned_copy_ajax_call = function(key, opt) {
// ----------------------------- context menus -----------------------------
// -------------------------------------------------------------------------
-ns.create_context_menus = function() {
- var general_actions = {
- sep98: "---------"
- , general_actions: { name: kivi.t8('Requirement spec actions'), className: 'context-menu-heading' }
- // , sep99: "---------"
- , create_pdf: { name: kivi.t8('Create PDF'), icon: "pdf", callback: kivi.requirement_spec.create_reqspec_pdf }
- , create_version: { name: kivi.t8('Create new version'), icon: "new", callback: kivi.requirement_spec.create_version, disabled: kivi.requirement_spec.disable_commands }
- , copy_reqspec: { name: kivi.t8('Copy requirement spec'), icon: "copy", callback: kivi.requirement_spec.copy_reqspec }
- , delete_reqspec: { name: kivi.t8('Delete requirement spec'), icon: "delete", callback: kivi.requirement_spec.delete_reqspec }
- };
+ns.create_context_menus = function(is_template) {
+ if (is_template) {
+ var general_actions = {
+ sep98: "---------"
+ , general_actions: { name: kivi.t8('Section template actions'), className: 'context-menu-heading' }
+ // , sep99: "---------"
+ , copy_reqspec: { name: kivi.t8('Copy section template'), icon: "copy", callback: kivi.requirement_spec.copy_reqspec }
+ , delete_reqspec: { name: kivi.t8('Delete section template'), icon: "delete", callback: kivi.requirement_spec.delete_reqspec }
+ };
+ var events = {};
+
+ } else { // if (is_template)
+ var general_actions = {
+ sep98: "---------"
+ , general_actions: { name: kivi.t8('Requirement spec actions'), className: 'context-menu-heading' }
+ // , sep99: "---------"
+ , create_pdf: { name: kivi.t8('Create PDF'), icon: "pdf", callback: kivi.requirement_spec.create_reqspec_pdf }
+ , create_version: { name: kivi.t8('Create new version'), icon: "new", callback: kivi.requirement_spec.create_version, disabled: kivi.requirement_spec.disable_commands }
+ , copy_reqspec: { name: kivi.t8('Copy requirement spec'), icon: "copy", callback: kivi.requirement_spec.copy_reqspec }
+ , delete_reqspec: { name: kivi.t8('Delete requirement spec'), icon: "delete", callback: kivi.requirement_spec.delete_reqspec }
+ };
+
+ var events = {
+ show: kivi.requirement_spec.text_block_popup_menu_shown
+ , hide: kivi.requirement_spec.text_block_popup_menu_hidden
+ };
+
+ $.contextMenu({
+ selector: '.text-block-context-menu',
+ events: {
+ show: kivi.requirement_spec.text_block_popup_menu_shown
+ , hide: kivi.requirement_spec.text_block_popup_menu_hidden
+ },
+ items: $.extend({
+ heading: { name: kivi.t8('Text block actions'), className: 'context-menu-heading' }
+ , add: { name: kivi.t8('Add text block'), icon: "add", callback: kivi.requirement_spec.standard_text_block_ajax_call }
+ , edit: { name: kivi.t8('Edit text block'), icon: "edit", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
+ , delete: { name: kivi.t8('Delete text block'), icon: "delete", callback: kivi.requirement_spec.ask_delete_text_block, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
+ , sep1: "---------"
+ , flag: { name: kivi.t8('Toggle marker'), icon: "flag", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
+ , sep2: "---------"
+ , copy: { name: kivi.t8('Copy'), icon: "copy", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
+ , paste: { name: kivi.t8('Paste'), icon: "paste", callback: kivi.requirement_spec.standard_text_block_ajax_call }
+ }, general_actions)
+ });
+
+ $.contextMenu({
+ selector: '.time-cost-estimate-context-menu',
+ items: $.extend({
+ heading: { name: kivi.t8('Time/cost estimate actions'), className: 'context-menu-heading' }
+ , edit: { name: kivi.t8('Edit'), icon: "edit", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
+ }, general_actions)
+ });
+
+ $.contextMenu({
+ selector: '.edit-time-cost-estimate-context-menu',
+ items: $.extend({
+ heading: { name: kivi.t8('Time/cost estimate actions'), className: 'context-menu-heading' }
+ , save: { name: kivi.t8('Save'), icon: "save", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
+ , cancel: { name: kivi.t8('Cancel'), icon: "close", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
+ }, general_actions)
+ });
+
+ $.contextMenu({
+ selector: '.versioned-copy-context-menu',
+ items: $.extend({
+ heading: { name: kivi.t8('Version actions'), className: 'context-menu-heading' }
+ , create_version_pdf: { name: kivi.t8('Create PDF'), icon: "pdf", callback: kivi.requirement_spec.create_pdf_for_versioned_copy_ajax_call }
+ , revert_to_version: { name: kivi.t8('Revert to version'), icon: "revert", callback: kivi.requirement_spec.revert_to_versioned_copy_ajax_call, disabled: kivi.requirement_spec.disable_versioned_copy_item_commands }
+ }, general_actions)
+ });
+ } // if (is_template) ... else ...
$.contextMenu({
selector: '#content',
items: general_actions
});
- var events = {
- show: kivi.requirement_spec.text_block_popup_menu_shown
- , hide: kivi.requirement_spec.text_block_popup_menu_hidden
- };
-
- $.contextMenu({
- selector: '.text-block-context-menu',
- events: {
- show: kivi.requirement_spec.text_block_popup_menu_shown
- , hide: kivi.requirement_spec.text_block_popup_menu_hidden
- },
- items: $.extend({
- heading: { name: kivi.t8('Text block actions'), className: 'context-menu-heading' }
- , add: { name: kivi.t8('Add text block'), icon: "add", callback: kivi.requirement_spec.standard_text_block_ajax_call }
- , edit: { name: kivi.t8('Edit text block'), icon: "edit", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
- , delete: { name: kivi.t8('Delete text block'), icon: "delete", callback: kivi.requirement_spec.ask_delete_text_block, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
- , sep1: "---------"
- , flag: { name: kivi.t8('Toggle marker'), icon: "flag", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
- , sep2: "---------"
- , copy: { name: kivi.t8('Copy'), icon: "copy", callback: kivi.requirement_spec.standard_text_block_ajax_call, disabled: kivi.requirement_spec.disable_edit_text_block_commands }
- , paste: { name: kivi.t8('Paste'), icon: "paste", callback: kivi.requirement_spec.standard_text_block_ajax_call }
- }, general_actions)
- });
-
events = {
show: kivi.requirement_spec.item_popup_menu_shown
, hide: kivi.requirement_spec.item_popup_menu_hidden
@@ -452,32 +490,6 @@ ns.create_context_menus = function() {
, paste: { name: kivi.t8('Paste'), icon: "paste", callback: kivi.requirement_spec.standard_item_ajax_call }
}, general_actions)
});
-
- $.contextMenu({
- selector: '.time-cost-estimate-context-menu',
- items: $.extend({
- heading: { name: kivi.t8('Time/cost estimate actions'), className: 'context-menu-heading' }
- , edit: { name: kivi.t8('Edit'), icon: "edit", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
- }, general_actions)
- });
-
- $.contextMenu({
- selector: '.edit-time-cost-estimate-context-menu',
- items: $.extend({
- heading: { name: kivi.t8('Time/cost estimate actions'), className: 'context-menu-heading' }
- , save: { name: kivi.t8('Save'), icon: "save", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
- , cancel: { name: kivi.t8('Cancel'), icon: "close", callback: kivi.requirement_spec.standard_time_cost_estimate_ajax_call }
- }, general_actions)
- });
-
- $.contextMenu({
- selector: '.versioned-copy-context-menu',
- items: $.extend({
- heading: { name: kivi.t8('Version actions'), className: 'context-menu-heading' }
- , create_version_pdf: { name: kivi.t8('Create PDF'), icon: "pdf", callback: kivi.requirement_spec.create_pdf_for_versioned_copy_ajax_call }
- , revert_to_version: { name: kivi.t8('Revert to version'), icon: "revert", callback: kivi.requirement_spec.revert_to_versioned_copy_ajax_call, disabled: kivi.requirement_spec.disable_versioned_copy_item_commands }
- }, general_actions)
- });
};
}); // end of namespace(...., function() {...
diff --git a/locale/de/all b/locale/de/all
index 5c154307b..50f66275f 100755
--- a/locale/de/all
+++ b/locale/de/all
@@ -152,6 +152,7 @@ $self->{texts} = {
'Add RFQ' => 'Preisanfrage erfassen',
'Add Request for Quotation' => 'Anfrage erfassen',
'Add Requirement Spec' => 'Pflichtenheft erfassen',
+ 'Add Requirement Spec Section Template' => 'Pflichtenheft-Abschnittsvorlage erfassen',
'Add Sales Delivery Order' => 'Lieferschein (Verkauf) erfassen',
'Add Sales Invoice' => 'Rechnung erfassen',
'Add Sales Order' => 'Auftrag erfassen',
@@ -521,6 +522,7 @@ $self->{texts} = {
'Copy file from #1 to #2 failed: #3' => 'Kopieren der Datei von #1 nach #2 schlug fehl: #3',
'Copy' => 'Kopieren',
'Copy requirement spec' => 'Pflichtenheft kopieren',
+ 'Copy section template' => 'Abschnittsvorlage kopieren',
'Correct taxkey' => 'Richtiger Steuerschlüssel',
'Cost' => 'Kosten',
'Costs' => 'Kosten',
@@ -551,6 +553,7 @@ $self->{texts} = {
'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 section template' => 'Eine neue Pflichtenheft-Abschnittsvorlage erfassen',
'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',
@@ -727,6 +730,7 @@ $self->{texts} = {
'Delete links' => 'Verknüpfungen löschen',
'Delete profile' => 'Profil löschen',
'Delete requirement spec' => 'Pflichtenheft löschen',
+ 'Delete section template' => 'Abschnittsvorlage löschen',
'Delete text block' => 'Textblock löschen',
'Delete transaction' => 'Buchung löschen',
'Deleted' => 'Gelöscht',
@@ -910,6 +914,7 @@ $self->{texts} = {
'Edit requirement spec type' => 'Pflichtenhefttypen bearbeiten',
'Edit risk level' => 'Risikograd bearbeiten',
'Edit section #1' => 'Abschnitt #1 bearbeiten',
+ 'Edit section template' => 'Abschnittsvorlage bearbeiten',
'Edit templates' => 'Vorlagen bearbeiten',
'Edit text block' => 'Textblock bearbeiten',
'Edit text block \'#1\'' => 'Textblock \'#1\' bearbeiten',
@@ -1865,6 +1870,7 @@ $self->{texts} = {
'Requested execution date to' => 'Gewünschtes Ausführungsdatum bis',
'Requests for Quotation' => 'Preisanfragen',
'Required by' => 'Lieferdatum',
+ 'Requirement Spec Section Templates' => 'Pflichtenheft-Abschnittsvorlagen',
'Requirement Spec Status' => 'Pflichtenheftstatus',
'Requirement Spec Statuses' => 'Pflichtenheftstatus',
'Requirement Spec Type' => 'Pflichtenhefttyp',
@@ -1873,6 +1879,7 @@ $self->{texts} = {
'Requirement spec actions' => 'Pflichtenheftaktionen',
'Requirement spec function block #1 with #2 sub function blocks; description: "#3"' => 'Pflichtenheft-Funktionsblock #1 mit #2 Unterfunktionsblöcken; Beschreibung: "#3"',
'Requirement spec section #1 "#2" with #3 function blocks and a total of #4 sub function blocks; preamble: "#5"' => 'Pflichtenheftabschnitt #1 "#2" mit #3 Funktionsblöcken und insgesamt #4 Unterfunktionsblöcken; Einleitung: "#5"',
+ 'Requirement spec section template \'#1\'' => 'Pflichtenheft_Abschnittsvorlage \'#1\'',
'Requirement spec sub function block #1; description: "#2"' => 'Pflichtenheft-Unterfunktionsblock #1; Beschreibung: "#2"',
'Requirement spec text block "#1"; content: "#2"' => 'Pflichtenheft-Textblock "1"; Inhalt: "#2"',
'Requirement specs' => 'Pflichtenhefte',
@@ -1962,6 +1969,7 @@ $self->{texts} = {
'Searchable' => 'Durchsuchbar',
'Secondary sorting' => 'Untersortierung',
'Section "#1"' => 'Abschnitt "#1"',
+ 'Section template actions' => 'Abschnittsvorlagen-Aktionen',
'Section/Function block actions' => 'Abschnitts-/Funktionsblockaktionen',
'Sections' => 'Abschnitte',
'Select' => 'auswählen',
@@ -2389,6 +2397,7 @@ $self->{texts} = {
'The risk level is in use and cannot be deleted.' => 'Der Risikograd wird verwendet und kann nicht gelöscht werden.',
'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.',
'The second way is to use Perl\'s CPAN module and let it download and install the module for you.' => 'Die zweite Variante besteht darin, Perls CPAN-Modul zu benutzen und es das Modul für Sie installieren zu lassen.',
+ 'The section template has been saved.' => 'Die Abschnittsvorlage wurde gespeichert.',
'The selected bank account does not exist anymore.' => 'Das ausgewählte Bankkonto existiert nicht mehr.',
'The selected bin does not exist.' => 'Der ausgewählte Lagerplatz existiert nicht.',
'The selected currency' => 'Die ausgewählte Währung',
diff --git a/menus/erp.ini b/menus/erp.ini
index a03f150bb..41a1ca652 100644
--- a/menus/erp.ini
+++ b/menus/erp.ini
@@ -35,6 +35,12 @@ ACCESS=project_edit
module=controller.pl
action=Project/new
+[Master Data--Add Requirement Spec Section Template]
+ACCESS=project_edit
+module=controller.pl
+action=RequirementSpec/new
+is_template=1
+
[Master Data--Update Prices]
ACCESS=part_service_assembly_edit
module=ic.pl
@@ -89,6 +95,11 @@ action=Project/list
filter.active=active
filter.valid=valid
+[Master Data--Reports--Requirement Spec Section Templates]
+module=controller.pl
+action=RequirementSpec/list
+is_template=1
+
[AR]
[AR--Add Quotation]
diff --git a/sql/Pg-upgrade2/requirement_specs_section_templates.sql b/sql/Pg-upgrade2/requirement_specs_section_templates.sql
new file mode 100644
index 000000000..a3bc1e62b
--- /dev/null
+++ b/sql/Pg-upgrade2/requirement_specs_section_templates.sql
@@ -0,0 +1,17 @@
+-- @tag: requirement_specs_section_templates
+-- @description: requirement_specs_section_templates
+-- @depends: release_3_0_0 requirement_specs
+-- @charset: utf-8
+
+ALTER TABLE requirement_specs ALTER COLUMN type_id DROP NOT NULL;
+ALTER TABLE requirement_specs ALTER COLUMN status_id DROP NOT NULL;
+ALTER TABLE requirement_specs ALTER COLUMN customer_id DROP NOT NULL;
+
+ALTER TABLE requirement_specs
+ADD CONSTRAINT requirement_specs_is_template_or_has_customer_status_type
+CHECK (
+ is_template
+ OR ( (type_id IS NOT NULL)
+ AND (status_id IS NOT NULL)
+ AND (customer_id IS NOT NULL))
+);
diff --git a/templates/webpages/requirement_spec/_filter.html b/templates/webpages/requirement_spec/_filter.html
index 33fc8631c..a2e570279 100644
--- a/templates/webpages/requirement_spec/_filter.html
+++ b/templates/webpages/requirement_spec/_filter.html
@@ -8,6 +8,7 @@
[% LxERP.t8("Hide Filter") %]
diff --git a/templates/webpages/requirement_spec/_form.html b/templates/webpages/requirement_spec/_form.html
index 18e99ad6c..800bb15ab 100644
--- a/templates/webpages/requirement_spec/_form.html
+++ b/templates/webpages/requirement_spec/_form.html
@@ -4,6 +4,7 @@
%]