Pflichtenhefte: PDFs zu Arbeitskopie und Versionen erzeugen
authorMoritz Bunkus <m.bunkus@linet-services.de>
Thu, 25 Apr 2013 11:44:11 +0000 (13:44 +0200)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 1 Apr 2014 11:03:20 +0000 (13:03 +0200)
19 files changed:
SL/Controller/RequirementSpec.pm
SL/Controller/RequirementSpecItem.pm
SL/DB/RequirementSpec.pm
SL/DB/RequirementSpecItem.pm
SL/Presenter/RequirementSpecItem.pm
css/requirement_spec.css
image/application-pdf.png [new file with mode: 0644]
js/requirement_spec.js
locale/de/all
sql/Pg-upgrade2/requirement_specs_print_templates.pl [new file with mode: 0644]
templates/print/Standard/images/draft.png [new file with mode: 0644]
templates/print/Standard/images/hintergrund_seite1.png [new file with mode: 0644]
templates/print/Standard/images/hintergrund_seite2.png [new file with mode: 0644]
templates/print/Standard/images/schachfiguren.jpg [new file with mode: 0644]
templates/print/Standard/kivitendo.sty [new file with mode: 0644]
templates/print/Standard/requirement_spec.tex [new file with mode: 0644]
templates/webpages/requirement_spec/show.html
templates/webpages/requirement_spec_item/_function_block.html
templates/webpages/requirement_spec_item/_section.html

index b557c03..aa259f6 100644 (file)
@@ -1,6 +1,7 @@
 package SL::Controller::RequirementSpec;
 
 use strict;
+use utf8;
 
 use parent qw(SL::Controller::Base);
 
@@ -20,6 +21,7 @@ use SL::DB::RequirementSpecType;
 use SL::DB::RequirementSpec;
 use SL::Helper::Flash;
 use SL::Locale::String;
+use SL::Template::LaTeX;
 
 use Rose::Object::MakeMethods::Generic
 (
@@ -193,6 +195,22 @@ sub action_revert_to {
   $self->js->redirect_to($self->url_for(action => 'show', id => $self->requirement_spec->id))->render($self);
 }
 
+sub action_create_pdf {
+  my ($self, %params) = @_;
+
+  my %result = SL::Template::LaTeX->parse_and_create_pdf('requirement_spec.tex', SELF => $self, rspec => $self->requirement_spec);
+
+  $::form->error(t8('Conversion to PDF failed: #1', $result{error})) if $result{error};
+
+  my $attachment_name  =  $self->requirement_spec->type->description . ' ' . ($self->requirement_spec->working_copy_id || $self->requirement_spec->id);
+  $attachment_name    .=  ' (v' . $self->requirement_spec->version->version_number . ')' if $self->requirement_spec->version;
+  $attachment_name    .=  '.pdf';
+  $attachment_name     =~ s/[^\wäöüÄÖÜß \-\+\(\)\[\]\{\}\.,]+/_/g;
+
+  $self->send_file($result{file_name}, type => 'application/pdf', name => $attachment_name);
+  unlink $result{file_name};
+}
+
 #
 # filters
 #
index cffc22c..98e160a 100644 (file)
@@ -542,8 +542,8 @@ sub create_dependencies {
 
   return map { [ $_->fb_number . ' ' . $_->title,
                  [ map { ( $self->create_dependency_item($_),
-                           map { $self->create_dependency_item($_, '->') } @{ $_->sorted_children })
-                       } @{ $_->sorted_children } ] ]
+                           map { $self->create_dependency_item($_, '->') } @{ $_->children_sorted })
+                       } @{ $_->children_sorted } ] ]
              } @{ $self->item->requirement_spec->sections };
 }
 
index 6a43a28..5cfb5dc 100644 (file)
@@ -8,6 +8,7 @@ use Rose::DB::Object::Helpers;
 use SL::DB::MetaSetup::RequirementSpec;
 use SL::DB::Manager::RequirementSpec;
 use SL::Locale::String;
+use SL::Util qw(_hashify);
 
 __PACKAGE__->meta->add_relationship(
   items            => {
@@ -49,26 +50,43 @@ sub _before_save_initialize_not_null_columns {
   return 1;
 }
 
-sub text_blocks_for_position {
-  my ($self, $output_position) = @_;
+sub text_blocks_sorted {
+  my ($self, %params) = _hashify(1, @_);
 
-  return [ sort { $a->position <=> $b->position } grep { $_->output_position == $output_position } @{ $self->text_blocks } ];
+  my @text_blocks = @{ $self->text_blocks };
+  @text_blocks    = grep { $_->output_position == $params{output_position} } @text_blocks if exists $params{output_position};
+  @text_blocks    = sort { $a->position        <=> $b->position            } @text_blocks;
+
+  return wantarray ? @text_blocks : \@text_blocks;
 }
 
-sub sections {
+sub sections_sorted {
   my ($self, @rest) = @_;
 
   croak "This sub is not a writer" if @rest;
 
-  return [ sort { $a->position <=> $b->position } grep { !$_->parent_id } @{ $self->items } ];
+  my @sections = sort { $a->position <=> $b->position } grep { !$_->parent_id } @{ $self->items };
+  return wantarray ? @sections : \@sections;
 }
 
+sub sections { &sections_sorted; }
+
 sub displayable_name {
   my ($self) = @_;
 
   return sprintf('%s: "%s"', $self->type->description, $self->title);
 }
 
+sub versioned_copies_sorted {
+  my ($self, %params) = _hashify(1, @_);
+
+  my @copies = @{ $self->versioned_copies };
+  @copies    = grep { $_->version->version_number <=  $params{max_version_number} } @copies if $params{max_version_number};
+  @copies    = sort { $a->version->version_number <=> $b->version->version_number } @copies;
+
+  return wantarray ? @copies : \@copies;
+}
+
 sub create_copy {
   my ($self, %params) = @_;
 
@@ -195,6 +213,8 @@ SQL
 sub create_version {
   my ($self, %attributes) = @_;
 
+  croak "Cannot work on a versioned copy" if $self->working_copy_id;
+
   my ($copy, $version);
   my $ok = $self->db->do_transaction(sub {
     delete $attributes{version_number};
@@ -213,8 +233,7 @@ sub create_version {
 sub invalidate_version {
   my ($self, %params) = @_;
 
-  $::lxdebug->message(0, "Invalidate version called for id " . $self->id . " version " . $self->version_id);
-  $::lxdebug->show_backtrace(1);
+  croak "Cannot work on a versioned copy" if $self->working_copy_id;
 
   return if !$self->id || !$self->version_id;
   $self->update_attributes(version_id => undef);
index 6926553..641982c 100644 (file)
@@ -99,12 +99,13 @@ sub validate {
   return @errors;
 }
 
-sub sorted_children {
+sub children_sorted {
   my ($self, @args) = @_;
 
   croak "Not a writer" if @args;
 
-  return [ sort { $a->position <=> $b->position } @{ $self->children } ];
+  my @children = sort { $a->position <=> $b->position } $self->children;
+  return wantarray ? @children : \@children;
 }
 
 sub section {
index f1c30bb..c9653e7 100644 (file)
@@ -18,7 +18,7 @@ sub requirement_spec_item_tree_node_title {
 sub requirement_spec_item_jstree_data {
   my ($self, $item, %params) = @_;
 
-  my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->sorted_children };
+  my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->children_sorted };
   my $type     = !$item->parent_id ? 'section' : 'function-block';
   my $class    = $type . '-context-menu';
   $class      .= ' flagged' if $item->is_flagged;
index 29b4582..cefdf19 100644 (file)
@@ -47,6 +47,7 @@ table.rs_input_field input, table.rs_input_field select {
 .context-menu-item.icon-close  { background-image: url("../image/document-close.png"); }
 .context-menu-item.icon-save   { background-image: url("../image/document-save.png"); }
 .context-menu-item.icon-revert { background-image: url("../image/edit-undo.png"); }
+.context-menu-item.icon-pdf    { background-image: url("../image/application-pdf.png"); }
 
 /* ------------------------------------------------------------ */
 /* Sections & function blocks */
diff --git a/image/application-pdf.png b/image/application-pdf.png
new file mode 100644 (file)
index 0000000..ad6a39f
Binary files /dev/null and b/image/application-pdf.png differ
index 6373b69..f0c472b 100644 (file)
@@ -293,9 +293,9 @@ function standard_time_cost_estimate_ajax_call(key, opt) {
 // ---------------------------- general actions ----------------------------
 // -------------------------------------------------------------------------
 
-function download_reqspec_pdf(key, opt) {
+function create_reqspec_pdf(key, opt) {
   var data = {
-    action: "RequirementSpec/download_pdf",
+    action: "RequirementSpec/create_pdf",
     id:     $('#requirement_spec_id').val()
   };
   $.download("controller.pl", data);
@@ -342,7 +342,11 @@ function create_requirement_spec_version() {
 }
 
 function create_pdf_for_versioned_copy_ajax_call(key, opt) {
-  // TODO: create_pdf_for_versioned_copy_ajax_call
+  var data = {
+    action: "RequirementSpec/create_pdf",
+    id:     find_versioned_copy_id(opt.$trigger)
+  };
+  $.download("controller.pl", data);
 
   return true;
 }
@@ -371,6 +375,7 @@ function create_requirement_spec_context_menus() {
       sep98:           "---------"
     , general_actions: { name: kivi.t8('Requirement spec actions'), className: 'context-menu-heading' }
     // , sep99:           "---------"
+    , create_pdf:      { name: kivi.t8('Create PDF'),              icon: "pdf",    callback: create_reqspec_pdf }
     , create_version:  { name: kivi.t8('Create new version'),      icon: "new",    callback: create_requirement_spec_version, disabled: disable_requirement_spec_commands }
     , copy_reqspec:    { name: kivi.t8('Copy requirement spec'),   icon: "copy",   callback: copy_reqspec   }
     , delete_reqspec:  { name: kivi.t8('Delete requirement spec'), icon: "delete", callback: delete_reqspec }
@@ -466,9 +471,9 @@ function create_requirement_spec_context_menus() {
   $.contextMenu({
     selector: '.versioned-copy-context-menu',
     items:    $.extend({
-        heading:           { name: kivi.t8('Version actions'), className: 'context-menu-heading' }
-        // create_pdf:        { name: kivi.t8('Create PDF'),        icon: "pdf",    callback: create_pdf_for_versioned_copy_ajax_call                                                }
-      , revert_to_version: { name: kivi.t8('Revert to version'), icon: "revert", callback: revert_to_versioned_copy_ajax_call,     disabled: disable_versioned_copy_item_commands }
+        heading:            { name: kivi.t8('Version actions'), className: 'context-menu-heading' }
+      , create_version_pdf: { name: kivi.t8('Create PDF'),        icon: "pdf",    callback: create_pdf_for_versioned_copy_ajax_call                                                }
+      , revert_to_version:  { name: kivi.t8('Revert to version'), icon: "revert", callback: revert_to_versioned_copy_ajax_call,     disabled: disable_versioned_copy_item_commands }
     }, general_actions)
   });
 }
index b341d98..77e9eb0 100755 (executable)
@@ -516,6 +516,7 @@ $self->{texts} = {
   'Continue'                    => 'Weiter',
   'Contra'                      => 'gegen',
   'Conversion of "birthday" contact person attribute' => 'Umstellung des Kontaktpersonenfeldes "Geburtstag"',
+  'Conversion to PDF failed: #1' => 'Konvertierung zu PDF schlug fehl: #1',
   'Copies'                      => 'Kopien',
   'Copy file from #1 to #2 failed: #3' => 'Kopieren der Datei von #1 nach #2 schlug fehl: #3',
   'Copy'                        => 'Kopieren',
diff --git a/sql/Pg-upgrade2/requirement_specs_print_templates.pl b/sql/Pg-upgrade2/requirement_specs_print_templates.pl
new file mode 100644 (file)
index 0000000..4dbe168
--- /dev/null
@@ -0,0 +1,22 @@
+# @tag: requirement_specs_print_templates
+# @description: requirement_specs_print_templates
+# @depends: requirement_specs
+package SL::DBUpgrade2::requirement_specs_print_templates;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  $self->add_print_templates(
+    'templates/print/Standard',
+    qw(images/draft.png images/hintergrund_seite1.png images/hintergrund_seite2.png images/schachfiguren.jpg kivitendo.sty requirement_spec.tex)
+  );
+
+  return 1;
+}
+
+1;
diff --git a/templates/print/Standard/images/draft.png b/templates/print/Standard/images/draft.png
new file mode 100644 (file)
index 0000000..8181beb
Binary files /dev/null and b/templates/print/Standard/images/draft.png differ
diff --git a/templates/print/Standard/images/hintergrund_seite1.png b/templates/print/Standard/images/hintergrund_seite1.png
new file mode 100644 (file)
index 0000000..28610f4
Binary files /dev/null and b/templates/print/Standard/images/hintergrund_seite1.png differ
diff --git a/templates/print/Standard/images/hintergrund_seite2.png b/templates/print/Standard/images/hintergrund_seite2.png
new file mode 100644 (file)
index 0000000..e4b204b
Binary files /dev/null and b/templates/print/Standard/images/hintergrund_seite2.png differ
diff --git a/templates/print/Standard/images/schachfiguren.jpg b/templates/print/Standard/images/schachfiguren.jpg
new file mode 100644 (file)
index 0000000..d8e9cca
Binary files /dev/null and b/templates/print/Standard/images/schachfiguren.jpg differ
diff --git a/templates/print/Standard/kivitendo.sty b/templates/print/Standard/kivitendo.sty
new file mode 100644 (file)
index 0000000..32d2e22
--- /dev/null
@@ -0,0 +1,182 @@
+\ProvidesFile{kivitendo.sty}
+\usepackage{colortbl}
+\usepackage{eurosym}
+\usepackage{german}
+\usepackage{graphicx}
+\usepackage{ifthen}
+\usepackage[utf8]{inputenc}
+\usepackage{latexsym}
+\usepackage{longtable}
+\usepackage{textcomp}
+
+%% Paketoptionen
+\newboolean{defaultbg}\setboolean{defaultbg}{true}
+\newboolean{draftbg}
+\newboolean{reqspeclogo}
+\newboolean{secondpagelogo}
+\DeclareOption{nologo}{\setboolean{defaultbg}{false}}
+\DeclareOption{draftlogo}{\setboolean{defaultbg}{false}\setboolean{draftbg}{true}}
+\DeclareOption{reqspeclogo}{\setboolean{reqspeclogo}{true}}
+\DeclareOption{secondpagelogo}{\setboolean{defaultbg}{false}\setboolean{secondpagelogo}{true}}
+\ProcessOptions
+
+%% Seitenlayout
+\setlength{\voffset}{-1.5cm}
+\setlength{\hoffset}{-2.5cm}
+\setlength{\topmargin}{0cm}
+\setlength{\headheight}{0.5cm}
+\setlength{\headsep}{1cm}
+\setlength{\topskip}{0pt}
+\setlength{\oddsidemargin}{2cm}
+\setlength{\textwidth}{16.4cm}
+\setlength{\textheight}{25cm}
+\setlength{\footskip}{1cm}
+\setlength{\parindent}{0pt}
+\setlength{\tabcolsep}{0.2cm}
+
+\setlength{\unitlength}{1cm}
+
+\newcommand{\kivitendobgsettings}{%
+  \setlength{\headsep}{2.5cm}
+  \setlength{\textheight}{22.5cm}
+  \setlength{\footskip}{0.9cm}
+}
+
+%% Standardschrift
+\newcommand{\defaultfont}{\fontfamily{cmss}\fontsize{10pt}{12pt}\fontseries{m}\selectfont}
+\renewcommand{\familydefault}{cmss}
+
+%% Checkboxen
+\newsavebox{\checkedbox}
+\savebox{\checkedbox}(0.2,0.4){
+  \put(-0.15,-0.425){$\times$}
+  \put(-0.15,-0.45){$\Box$}
+}
+\newsavebox{\uncheckedbox}
+\savebox{\uncheckedbox}(0.2,0.4){
+  \put(-0.15,-0.45){$\Box$}
+}
+
+%% Farben
+\definecolor{kivitendoorange}{rgb}{1,0.4,0.2}
+\definecolor{kivitendodarkred}{rgb}{0.49,0,0}
+\definecolor{kivitendoyellow}{rgb}{1,1,0.4}
+\definecolor{kivitendobggray}{gray}{0.9}
+\definecolor{kivitendowhite}{gray}{1}
+
+%% Kopf- und Fußzeilen
+\newcommand{\kivitendofirsthead}{}
+\newcommand{\kivitendofirstfoot}{}
+\newcommand{\kivitendosecondhead}{}
+\newcommand{\kivitendosecondfoot}{\centerline{\defaultfont\small Seite \thepage}}
+
+\newcommand{\myhead}{%
+  \ifthenelse{\boolean{defaultbg}}{%
+    \begin{picture}(0,0)
+      \put(-2.025,-28.1){\includegraphics*[width=\paperwidth,keepaspectratio=true]{images/hintergrund_seite1.png}}
+    \end{picture}%
+  }{}%
+  \ifthenelse{\boolean{secondpagelogo}}{%
+    \begin{picture}(0,0)
+      \put(-2.025,-28.1){\includegraphics*[width=\paperwidth,keepaspectratio=true]{images/hintergrund_seite2.png}}
+    \end{picture}%
+  }{}%
+  \ifthenelse{\boolean{draftbg}}{%
+    \begin{picture}(0,0)
+      \put(-2.025,-26.9){\includegraphics*[width=\paperwidth,keepaspectratio=true]{images/draft.png}}
+    \end{picture}%
+  }{}%
+  \ifthenelse{\boolean{reqspeclogo}}{%
+    \begin{picture}(0,0)
+      \put(3,-22){\includegraphics*[width=13cm,keepaspectratio=true]{images/schachfiguren.jpg}}
+      \put(0.275,-4.1){\colorbox{kivitendoorange}{\begin{minipage}[t][4.5cm]{2.5cm}\hspace*{2.5cm}\end{minipage}}}
+      \put(0.275,-8.8){\colorbox{kivitendodarkred}{\begin{minipage}[t][4.5cm]{2.5cm}\hspace*{2.5cm}\end{minipage}}}
+      \put(0.275,-13.5){\colorbox{kivitendoyellow}{\begin{minipage}[t][4.5cm]{2.5cm}\hspace*{2.5cm}\end{minipage}}}
+    \end{picture}%
+  }{}%
+  \kivitendofirsthead
+}
+
+\newcommand{\mysecondhead}{%
+  \ifthenelse{\boolean{defaultbg} \or \boolean{secondpagelogo}}{%
+    \begin{picture}(0,0)
+      \put(-2.025,-28.1){\includegraphics*[width=\paperwidth,keepaspectratio=true]{images/hintergrund_seite2.png}}
+    \end{picture}%
+  }{}%
+  \ifthenelse{\boolean{draftbg}}{%
+    \begin{picture}(0,0)
+      \put(-2.025,-26.9){\includegraphics*[width=\paperwidth,keepaspectratio=true]{images/draft.png}}
+    \end{picture}%
+  }{}%
+  \kivitendosecondhead
+}
+
+\newcommand{\myfoot}{\kivitendofirstfoot}
+\newcommand{\mysecondfoot}{\kivitendosecondfoot}
+
+\renewcommand{\ps@headings}{%
+  \renewcommand{\@oddhead}{\myhead}
+  \renewcommand{\@evenhead}{\@oddhead}%
+  \renewcommand{\@oddfoot}{\myfoot}
+  \renewcommand{\@evenfoot}{\@oddfoot}%
+}
+
+\renewcommand{\ps@plain}{%
+  \renewcommand{\@oddhead}{\mysecondhead}
+  \renewcommand{\@evenhead}{\@oddhead}%
+  \renewcommand{\@oddfoot}{\mysecondfoot}
+  \renewcommand{\@evenfoot}{\@oddfoot}%
+}
+
+\pagestyle{plain}
+\thispagestyle{headings}
+
+% Abschnitte mit Kasten hinterlegt
+
+\newcommand{\reqspecsectionstyle}{%
+\renewcommand{\thesection}{\alph{section}}
+\makeatletter
+\def\section{\@ifstar\unnumberedsection\numberedsection}
+\makeatother
+}
+
+\makeatletter
+\def\numberedsection{\@ifnextchar[%]
+  \numberedsectionwithtwoarguments\numberedsectionwithoneargument}
+\def\unnumberedsection{\@ifnextchar[%]
+  \unnumberedsectionwithtwoarguments\unnumberedsectionwithoneargument}
+\def\numberedsectionwithoneargument#1{\numberedsectionwithtwoarguments[#1]{#1}}
+\def\unnumberedsectionwithoneargument#1{\unnumberedsectionwithtwoarguments[#1]{#1}}
+\def\numberedsectionwithtwoarguments[#1]#2{%
+  \ifhmode\par\fi
+  \removelastskip
+  \vskip 3ex\goodbreak
+  \refstepcounter{section}%
+  \noindent
+  \begingroup
+  \leavevmode\Large\bfseries\raggedright
+  \begin{picture}(0,0)
+    \put(0,0){\colorbox{kivitendoorange}{\parbox{0.7cm}{\hspace*{0.7cm}\\\vspace*{0.2cm}}}}
+  \end{picture}%
+  \hspace*{0.3cm}\textcolor{white}{\thesection{}.}%
+  \quad%
+  #2
+  \par
+  \endgroup
+  \vskip 2ex\nobreak
+  \addcontentsline{toc}{section}{\protect\numberline{\thesection{}.}#1}%
+  }
+\def\unnumberedsectionwithtwoarguments[#1]#2{%
+  \ifhmode\par\fi
+  \removelastskip
+  \vskip 3ex\goodbreak
+  \noindent
+  \begingroup
+  \leavevmode\Large\bfseries\raggedright
+  \leavevmode\Large\bfseries\raggedright
+  #2
+  \par
+  \endgroup
+  \vskip 2ex\nobreak%
+}
+\makeatother
diff --git a/templates/print/Standard/requirement_spec.tex b/templates/print/Standard/requirement_spec.tex
new file mode 100644 (file)
index 0000000..6580a8e
--- /dev/null
@@ -0,0 +1,162 @@
+% config: use-template-toolkit=1
+% config: tag-style=$( )$
+$( USE LxLatex )$
+$( USE P )$
+\documentclass{scrartcl}
+
+\usepackage[reqspeclogo,$( IF !rspec.version_id )$draftlogo$( ELSE )$secondpagelogo$( END )$]{kivitendo}
+
+\kivitendobgsettings
+
+\setlength{\LTpre}{0pt}
+\setlength{\LTpost}{0pt}
+
+\renewcommand{\kivitendosecondfoot}{%
+  \parbox{12cm}{%
+    \defaultfont\scriptsize%
+    $( LxLatex.filter(rspec.displayable_name) )$\\
+    $( !rspec.version_id ? "Arbeitskopie ohne Version" : "Version " _ rspec.version.version_number _ " vom " _ rspec.version.itime.to_kivitendo(precision='minute') )$
+
+    \vspace*{0.2cm}%
+    Seite \thepage%
+  }%
+}
+
+\reqspecsectionstyle
+
+\begin{document}
+
+%% Titelseite
+
+\setlongtables
+\defaultfont
+
+\begin{picture}(0,0)
+  \put(3.5,-5){%
+    \begin{minipage}[t][6cm]{12cm}
+      \Large
+      \textcolor{kivitendodarkred}{$( LxLatex.filter(rspec.type.description) )$}
+
+      \huge
+      $( LxLatex.filter(rspec.customer.name) )$
+
+      \vspace*{0.5cm}
+      \Large
+      $( LxLatex.filter(rspec.title) )$
+      \normalsize
+%$( IF rspec.version_id )$
+
+    Version $( LxLatex.filter(rspec.version.version_number) )$
+%$( END )$
+    \end{minipage}%
+  }
+\end{picture}
+
+%% Inhaltsverzeichnis
+
+%\newpage
+
+%\tableofcontents
+
+%% Versionen
+\newpage
+
+\section{Versionen}
+
+\vspace*{0.7cm}
+
+%$( SET working_copy     = rspec.working_copy_id ? rspec.working_copy : rspec )$
+%$( SET versioned_copies = rspec.version_id ? working_copy.versioned_copies_sorted(max_version_number = rspec.version.version_number) : working_copy.versioned_copies_sorted )$
+%$( IF !versioned_copies.size )$
+  Bisher wurden noch keine Versionen angelegt.
+%$( ELSE )$
+\begin{longtable}{|p{2cm}|p{2cm}|p{12cm}|}
+  \hline
+  \multicolumn{1}{|r}{\small Version} &
+  \multicolumn{1}{|r|}{\small Datum} &
+  \small Beschreibung\\
+  \hline
+%$( FOREACH versioned_copy = versioned_copies )$
+   \multicolumn{1}{|r}{\small $( LxLatex.filter(versioned_copy.version.version_number) )$} &
+   \multicolumn{1}{|r|}{\small $( LxLatex.filter(versioned_copy.version.itime.to_kivitendo(precision='minute')) )$} &
+   \small $( LxLatex.filter(versioned_copy.version.description) )$\\
+%$( END )$
+  \hline
+\end{longtable}
+%$( END )$
+
+%$( BLOCK text_block_outputter )$
+%  $( SET text_blocks = rspec.text_blocks_sorted(output_position=output_position) )$
+%  $( IF text_blocks.size )$
+
+  \newpage
+
+  \section{$( heading )$}
+
+%    $( FOREACH text_block = text_blocks )$
+
+    \subsection{$( LxLatex.filter(text_block.title) )$}
+
+$( LxLatex.filter(text_block.text) )$
+
+%    $( END )$
+%  $( END )$
+%$( END )$
+
+%% Textblöcke davor
+$( PROCESS text_block_outputter output_position=0 heading='Allgemeines' )$
+
+%% Abschnitte und Funktionsblöcke
+\newpage
+
+\section{Spezifikation}
+
+\setlength{\LTpre}{-0.3cm}
+
+
+%$( FOREACH top_item = rspec.sections_sorted )$
+
+  \subsection{Abschnitt $( LxLatex.filter(top_item.fb_number) )$: $( LxLatex.filter(top_item.title) )$}
+
+%  $( IF top_item.description )$
+    $( LxLatex.filter(top_item.description.replace('\r', '').replace('\n+\Z', '')) )$
+
+    \vspace{0.5cm}
+%  $( END )$
+%  $( FOREACH item = top_item.children_sorted )$
+\parbox[t]{1.0cm}{\textcolor{kivitendodarkred}{$>>>$}}%
+\parbox[t]{15.0cm}{%
+\begin{longtable}{p{2.8cm}p{11.7cm}}
+  Funktionsblock & $( LxLatex.filter(item.fb_number) )$\\
+  Beschreibung & $( LxLatex.filter(item.description) )$\\
+  Abhängigkeiten & $( LxLatex.filter(P.requirement_spec_item_dependency_list(item)) )$
+\end{longtable}}
+
+%    $( FOREACH sub_item = item.children_sorted )$
+\hspace*{1.15cm}\rule{15.2cm}{0.2pt}\\
+\hspace*{1.0cm}%
+\parbox[t]{15.0cm}{%
+\begin{longtable}{p{2.8cm}p{11.7cm}}
+  Unterfunktionsblock & $( LxLatex.filter(sub_item.fb_number) )$\\
+  Beschreibung & $( LxLatex.filter(sub_item.description) )$\\
+  Abhängigkeiten & $( LxLatex.filter(P.requirement_spec_item_dependency_list(sub_item)) )$
+\end{longtable}}
+
+%    $( END )$
+
+%    $( UNLESS loop.last )$
+\vspace{0.2cm}
+\hrule
+\vspace{0.4cm}
+
+%    $( END )$
+
+%  $( END )$
+%
+%$( END )$
+
+%% Textblöcke dahinter
+$( PROCESS text_block_outputter output_position=1 heading='Weitere Punkte' )$
+
+
+\end{document}
index 395e394..f861cb1 100644 (file)
@@ -48,7 +48,7 @@ $(function() {
       metadata: { type: "text-blocks-front" },
       attr:     { id: "tb-front", class: "text-block-context-menu" },
       children: [
-[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(0) %]
+[% FOREACH tb = SELF.requirement_spec.text_blocks_sorted(output_position=0) %]
  [% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %]
 [% END %]
       ]},
@@ -66,7 +66,7 @@ $(function() {
       metadata: { type: "text-blocks-back" },
       attr:     { id: "tb-back", class: "text-block-context-menu" },
       children: [
-[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(1) %]
+[% FOREACH tb = SELF.requirement_spec.text_blocks_sorted(output_position=1) %]
  [% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %]
 [% END %]
       ]}];
index 4105838..a580ace 100644 (file)
@@ -9,7 +9,7 @@
    <div class="sub-function-block-header" id="sub-function-block_header_[%- requirement_spec_item.id -%]">
     [%- LxERP.t8("Sub function blocks") -%]
    </div>
-   [%- FOREACH sub_function_block = requirement_spec_item.sorted_children -%]
+   [%- FOREACH sub_function_block = requirement_spec_item.children_sorted -%]
     [%- INCLUDE 'requirement_spec_item/_sub_function_block.html' requirement_spec_item=sub_function_block -%]
    [%- END -%]
   </div>
index 8e7699d..dfa2063 100644 (file)
@@ -10,7 +10,7 @@
  </div>
 
  <div id="section-list" class="section">
-  [%- FOREACH function_block = requirement_spec_item.sorted_children -%]
+  [%- FOREACH function_block = requirement_spec_item.children_sorted -%]
    [%- INCLUDE 'requirement_spec_item/_function_block.html' requirement_spec_item=function_block -%]
   [%- END -%]
  </div>