Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision c19b1e03

Von Moritz Bunkus vor mehr als 10 Jahren hinzugefügt

  • ID c19b1e03fb03f195d86a5b78f8ce2338f745f599
  • Vorgänger ecce066d
  • Nachfolger 405a41ef

Pflichtenhefte: Zeit- und Kostenschätzungsmaske

Unterschiede anzeigen:

SL/Controller/Helper/RequirementSpec.pm
1
package SL::Controller::Helper::RequirementSpec;
2

  
3
use strict;
4

  
5
use Exporter qw(import);
6
our @EXPORT = qw(init_visible_section);
7

  
8
use SL::DB::Manager::RequirementSpecItem;
9

  
10
sub init_visible_section {
11
  my ($self)       = @_;
12

  
13
  my $content_id   = $::form->{current_content_id};
14
  my $content_type = $::form->{current_content_type};
15

  
16
  return undef unless $content_id;
17
  return undef unless $content_type =~ m/section|function-block/;
18

  
19
  $self->visible_item(SL::DB::Manager::RequirementSpecItem->find_by(id => $content_id));
20
  return undef unless $self->visible_item;
21

  
22
  return $self->visible_section($self->visible_item->section);
23
}
24

  
25
1;
SL/Controller/RequirementSpec.pm
10 10
use SL::Controller::Helper::Sorted;
11 11
use SL::Controller::Helper::ParseFilter;
12 12
use SL::Controller::Helper::ReportGenerator;
13
use SL::Controller::Helper::RequirementSpec;
13 14
use SL::DB::Customer;
14 15
use SL::DB::Project;
16
use SL::DB::RequirementSpecComplexity;
17
use SL::DB::RequirementSpecRisk;
15 18
use SL::DB::RequirementSpecStatus;
16 19
use SL::DB::RequirementSpecType;
17 20
use SL::DB::RequirementSpec;
......
20 23

  
21 24
use Rose::Object::MakeMethods::Generic
22 25
(
23
 scalar => [ qw(requirement_spec requirement_spec_item customers projects types statuses db_args flat_filter is_template) ],
26
  scalar                  => [ qw(requirement_spec_item customers types statuses db_args flat_filter is_template visible_item visible_section) ],
27
  'scalar --get_set_init' => [ qw(requirement_spec complexities risks projects) ],
24 28
);
25 29

  
26 30
__PACKAGE__->run_before('setup');
27
__PACKAGE__->run_before('load_requirement_spec',      only => [ qw(    ajax_edit        update show destroy) ]);
28
__PACKAGE__->run_before('load_select_options',        only => [ qw(new ajax_edit create update list) ]);
29
__PACKAGE__->run_before('load_search_select_options', only => [ qw(                            list) ]);
31
__PACKAGE__->run_before('load_select_options',  only => [ qw(new ajax_edit create update list) ]);
30 32

  
31 33
__PACKAGE__->get_models_url_params('flat_filter');
32 34
__PACKAGE__->make_paginated(
......
53 55
# actions
54 56
#
55 57

  
58

  
56 59
sub action_list {
57 60
  my ($self) = @_;
58 61

  
......
79 82
  $self->render('requirement_spec/_form', { layout => 0 }, submit_as => 'ajax');
80 83
}
81 84

  
85
sub action_ajax_show_time_and_cost_estimate {
86
  my ($self) = @_;
87

  
88
  $self->render('requirement_spec/_show_time_and_cost_estimate', { layout => 0 });
89
}
90

  
91
sub action_ajax_cancel_time_and_cost_estimate {
92
  my ($self) = @_;
93

  
94
  my $html   = $self->render('requirement_spec/_show_time_and_cost_estimate', { output => 0 });
95

  
96
  SL::ClientJS->new
97
    ->replaceWith('#time_cost_estimate', $html)
98
    ->render($self);
99
}
100

  
101
sub action_ajax_edit_time_and_cost_estimate {
102
  my ($self) = @_;
103

  
104
  my $html   = $self->render('requirement_spec/_edit_time_and_cost_estimate', { output => 0 });
105

  
106
  SL::ClientJS->new
107
    ->replaceWith('#time_cost_estimate', $html)
108
    ->render($self);
109
}
110

  
111
sub action_ajax_save_time_and_cost_estimate {
112
  my ($self) = @_;
113

  
114
  $self->requirement_spec->db->do_transaction(sub {
115
    # Make Emacs happy
116
    1;
117
    foreach my $attributes (@{ $::form->{requirement_spec_items} || [] }) {
118
      SL::DB::RequirementSpecItem
119
        ->new(id => delete $attributes->{id})
120
        ->load
121
        ->update_attributes(%{ $attributes });
122
    }
123

  
124
    1;
125
  });
126

  
127
  my $html = $self->render('requirement_spec/_show_time_and_cost_estimate', { output => 0 });
128
  my $js   = SL::ClientJS->new->replaceWith('#time_cost_estimate', $html);
129

  
130
  if ($self->visible_section) {
131
    $html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $self->visible_section);
132
    $js->html('#column-content', $html);
133
  }
134

  
135
  $js->render($self);
136
}
137

  
82 138
sub action_show {
83 139
  my ($self) = @_;
84 140

  
......
131 187
  $::request->{layout}->use_stylesheet("${_}.css") for qw(jquery.contextMenu requirement_spec);
132 188
  $::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree jquery/jquery.contextMenu client_js requirement_spec);
133 189
  $self->is_template($::form->{is_template} ? 1 : 0);
190
  $self->init_visible_section;
134 191

  
135 192
  return 1;
136 193
}
137 194

  
138
sub load_requirement_spec {
195
sub init_complexities {
196
  my ($self) = @_;
197
  return SL::DB::Manager::RequirementSpecComplexity->get_all_sorted;
198
}
199

  
200
sub init_risks {
201
  my ($self) = @_;
202
  return SL::DB::Manager::RequirementSpecRisk->get_all_sorted;
203
}
204

  
205
sub init_projects {
206
  my ($self) = @_;
207
  $self->projects(SL::DB::Manager::Project->get_all_sorted);
208
}
209

  
210
sub init_requirement_spec {
139 211
  my ($self) = @_;
140
  $self->requirement_spec(SL::DB::RequirementSpec->new(id => $::form->{id})->load || die "No such requirement spec");
212
  $self->requirement_spec(SL::DB::RequirementSpec->new(id => $::form->{id})->load || die "No such requirement spec") if $::form->{id};
141 213
}
142 214

  
143 215
sub load_select_options {
144 216
  my ($self) = @_;
145 217

  
146 218
  my @filter = ('!obsolete' => 1);
147
  if ($self->requirement_spec && $self->requirement_spec->customer_id) {
148
    @filter = ( or => [ @filter, id => $self->requirement_spec->customer_id ] );
149
  }
219
  @filter    = ( or => [ @filter, id => $self->requirement_spec->customer_id ] ) if $self->requirement_spec && $self->requirement_spec->customer_id;
150 220

  
151 221
  $self->customers(SL::DB::Manager::Customer->get_all_sorted(where => \@filter));
152 222
  $self->statuses( SL::DB::Manager::RequirementSpecStatus->get_all_sorted);
153 223
  $self->types(    SL::DB::Manager::RequirementSpecType->get_all_sorted);
154 224
}
155 225

  
156
sub load_search_select_options {
157
  my ($self) = @_;
158

  
159
  $self->projects(SL::DB::Manager::Project->get_all_sorted);
160
}
161

  
162 226
#
163 227
# helpers
164 228
#
SL/Controller/RequirementSpecItem.pm
10 10
use Time::HiRes ();
11 11

  
12 12
use SL::Clipboard;
13
use SL::Controller::Helper::RequirementSpec;
13 14
use SL::DB::RequirementSpec;
14 15
use SL::DB::RequirementSpecComplexity;
15 16
use SL::DB::RequirementSpecItem;
......
490 491
  return join "\n", (split m/\n/, $@)[0..4];
491 492
}
492 493

  
493
sub init_visible_section {
494
  my ($self)       = @_;
495

  
496
  my $content_id   = $::form->{current_content_id};
497
  my $content_type = $::form->{current_content_type};
498

  
499
  return undef unless $content_id;
500
  return undef unless $content_type =~ m/section|function-block/;
501

  
502
  $self->visible_item(SL::DB::Manager::RequirementSpecItem->find_by(id => $content_id));
503
  return undef unless $self->visible_item;
504

  
505
  return $self->visible_section($self->visible_item->section);
506
}
507

  
508 494
sub init_complexities {
509 495
  my ($self) = @_;
510 496

  
SL/Controller/Test.pm
1
package SL::Controller::Test;
2

  
3
use strict;
4

  
5
use parent qw(SL::Controller::Base);
6

  
7
use Data::Dumper;
8
use SL::ClientJS;
9

  
10
sub action_dump_form {
11
  my ($self) = @_;
12

  
13
  my $output = Dumper($::form);
14
  $self->render(\$output, { type => 'text' });
15
}
16

  
17
1;
css/requirement_spec.css
43 43
/* Special things that apply to the context menu */
44 44
/* ------------------------------------------------------------ */
45 45

  
46
.context-menu-item.icon-flag { background-image: url("../image/flag-red.png"); }
47

  
46
.context-menu-item.icon-flag  { background-image: url("../image/flag-red.png"); }
47
.context-menu-item.icon-close { background-image: url("../image/document-close.png"); }
48
.context-menu-item.icon-save  { background-image: url("../image/document-save.png"); }
48 49

  
49 50
/* ------------------------------------------------------------ */
50 51
/* Sections & function blocks */
js/locale/de.js
5 5
"Add sub function block":"Unterfunktionsblock hinzufügen",
6 6
"Add text block":"Textblock erfassen",
7 7
"Are you sure?":"Sind Sie sicher?",
8
"Copy":"Kopieren",
8 9
"Database Connection Test":"Test der Datenbankverbindung",
10
"Delete text block":"Textblock löschen",
11
"Delete":"Löschen",
12
"Do you really want to cancel?":"Wollen Sie wirklich abbrechen?",
9 13
"Do you want to set the account number \"#1\" to \"#2\" and the name \"#3\" to \"#4\"?":"Soll die Kontonummer \"#1\" zu \"#2\" und den Name \"#3\" zu \"#4\" geändert werden?",
14
"Edit text block":"Textblock bearbeiten",
15
"Edit":"Bearbeiten",
10 16
"Enter longdescription":"Langtext eingeben",
11 17
"Map":"Karte",
12 18
"Part picker":"Artikelauswahl",
19
"Paste":"Einfügen",
20
"Save":"Speichern",
13 21
"The description is missing.":"Die Beschreibung fehlt.",
14 22
"The name is missing.":"Der Name fehlt.",
15 23
"The name must only consist of letters, numbers and underscores and start with a letter.":"Der Name darf nur aus Buchstaben (keine Umlaute), Ziffern und Unterstrichen bestehen und muss mit einem Buchstaben beginnen.",
16 24
"The option field is empty.":"Das Optionsfeld ist leer.",
17 25
"The selected database is still configured for client \"#1\". If you delete the database that client will stop working until you re-configure it. Do you still want to delete the database?":"Die auswählte Datenbank ist noch für Mandant \"#1\" konfiguriert. Wenn Sie die Datenbank löschen, wird der Mandanten nicht mehr funktionieren, bis er anders konfiguriert wurde. Wollen Sie die Datenbank trotzdem löschen?"
18
"Copy":"Kopieren",
19
"Delete":"Löschen",
20
"Delete text block":"Textblock löschen",
21
"Edit":"Bearbeiten",
22
"Edit text block":"Textblock bearbeiten",
23
"Paste":"Einfügen",
24 26
"Toggle marker":"Markierung umschalten"
25 27
});
js/requirement_spec.js
268 268
  return handle_item_popup_menu_markings(opt, false);
269 269
}
270 270

  
271
// -------------------------------------------------------------------------
272
// -------------------------- time/cost estimate ---------------------------
273
// -------------------------------------------------------------------------
274

  
275
function standard_time_cost_estimate_ajax_call(key, opt) {
276
  if ((key == 'cancel') && !confirm(kivi.t8('Do you really want to cancel?')))
277
    return true;
278

  
279
  var data = "action=RequirementSpec/ajax_" + key + "_time_and_cost_estimate&";
280

  
281
  if (key == 'save')
282
    data += $('#edit_time_cost_estimate_form').serialize()
283
         +  '&' + $('#current_content_type').serialize()
284
         +  '&' + $('#current_content_id').serialize();
285
  else
286
    data += 'id=' + encodeURIComponent($('#requirement_spec_id').val());
287

  
288
  $.post("controller.pl", data, eval_json_result);
289

  
290
  return true;
291
}
292

  
293
// -------------------------------------------------------------------------
294
// ----------------------------- context menus -----------------------------
295
// -------------------------------------------------------------------------
296

  
271 297
function create_requirement_spec_context_menus() {
272 298
  var events = {
273 299
    show: requirement_spec_text_block_popup_menu_shown,
......
330 356
      , paste:                  { name: kivi.t8('Paste'),                  icon: "paste",  callback: standard_item_ajax_call }
331 357
    }
332 358
  });
359

  
360
  $.contextMenu({
361
    selector: '.time-cost-estimate-context-menu',
362
    events:   events,
363
    items:    { edit: { name: kivi.t8('Edit'), icon: "edit", callback: standard_time_cost_estimate_ajax_call } }
364
  });
365

  
366
  $.contextMenu({
367
    selector: '.edit-time-cost-estimate-context-menu',
368
    events:   events,
369
    items:    {
370
        save:   { name: kivi.t8('Save'),   icon: "save",  callback: standard_time_cost_estimate_ajax_call }
371
      , cancel: { name: kivi.t8('Cancel'), icon: "close", callback: standard_time_cost_estimate_ajax_call }
372
    }
373
  });
333 374
}
locale/de/all
514 514
  'Copy file from #1 to #2 failed: #3' => 'Kopieren der Datei von #1 nach #2 schlug fehl: #3',
515 515
  'Copy'                        => 'Kopieren',
516 516
  'Correct taxkey'              => 'Richtiger Steuerschlüssel',
517
  'Cost'                        => 'Kosten',
517 518
  'Costs'                       => 'Kosten',
518 519
  'Could not load class #1 (#2): "#3"' => 'Konnte Klasse #1 (#2) nicht laden: "#3"',
519 520
  'Could not load class #1, #2' => 'Konnte Klasse #1 nicht laden: "#2"',
......
765 766
  'Do not change the tax rate of taxkey 0.' => 'Ändern Sie nicht den Steuersatz vom Steuerschlüssel 0.',
766 767
  'Do not check for duplicates' => 'Nicht nach Dubletten suchen',
767 768
  'Do not set default buchungsgruppe' => 'Nie Standardbuchungsgruppe setzen',
769
  'Do you really want to cancel?' => 'Wollen Sie wirklich abbrechen?',
768 770
  'Do you really want to close the following SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.' => 'Wollen Sie wirklich die folgenden SEPA-Exporte abschließen? Für Überweisungen, die noch nicht gebucht wurden, werden dann keine Zahlungen verbucht.',
769 771
  'Do you really want to close the following SEPA exports? No payment will be recorded for bank transfers that haven\'t been marked as executed yet.' => 'Wollen Sie wirklich die folgenden SEPA-Exporte abschließen? Für Überweisungen, die noch nicht gebucht wurden, werden dann keine Zahlungen verbucht.',
770 772
  'Do you really want to delete AP transaction #1?' => 'Wollen Sie wirklich die Kreditorenbuchung #1 löschen?',
......
1064 1066
  'Full Access'                 => 'Vollzugriff',
1065 1067
  'Full Preview'                => 'Alles',
1066 1068
  'Full access to all functions' => 'Vollzugriff auf alle Funktionen',
1069
  'Function block'              => 'Funktionsblock',
1067 1070
  'Function/position'           => 'Funktion/Position',
1068 1071
  'Fwd'                         => 'Vorwärts',
1069 1072
  'GL Transaction'              => 'Dialogbuchung',
......
1395 1398
  'Name and Street'             => 'Name und Straße',
1396 1399
  'National Expenses'           => 'Aufwand Inland',
1397 1400
  'National Revenues'           => 'Erlöse Inland',
1401
  'Neither sections nor function blocks have been created yet.' => 'Es wurden bisher weder Abschnitte noch Funktionsblöcke angelegt.',
1398 1402
  'Net Income Statement'        => 'Einnahmenüberschußrechnung',
1399 1403
  'Net amount'                  => 'Nettobetrag',
1400 1404
  'Net amount (for verification)' => 'Nettobetrag (zur Überprüfung)',
......
2091 2095
  'Such entries cannot be exported into the DATEV format and have to be fixed as well.' => 'Solche Einträge sind aber nicht DATEV-exportiertbar und müssen ebenfalls korrigiert werden.',
2092 2096
  'Sum Credit'                  => 'Summe Haben',
2093 2097
  'Sum Debit'                   => 'Summe Soll',
2098
  'Sum for #1'                  => 'Summe für #1',
2099
  'Sum for section'             => 'Summe für Abschnitt',
2094 2100
  'Sum for'                     => 'Summe für',
2095 2101
  'Sum open amount'             => 'Summierter offener Betrag',
2096 2102
  'Sum per'                     => 'Summe per',
......
2459 2465
  'Three Options:'              => 'Drei Optionen:',
2460 2466
  'Time Format'                 => 'Uhrzeitformat',
2461 2467
  'Time Tracking'               => 'Zeiterfassung',
2468
  'Time and cost estimate'      => 'Zeit- und Kostenschätzung',
2462 2469
  'Time estimate'               => 'Zeitschätzung',
2463 2470
  'Time period for the analysis:' => 'Analysezeitraum:',
2464 2471
  'Timestamp'                   => 'Uhrzeit',
templates/webpages/requirement_spec/_edit_time_and_cost_estimate.html
1
[%- USE LxERP -%][%- USE L -%][%- USE HTML -%][%- USE P -%]
2
[%- DEFAULT id_prefix = 'time_and_cost_estimate_form' %]
3

  
4
<div id="time_cost_estimate" class="edit-time-cost-estimate-context-menu">
5
 [%- IF !SELF.requirement_spec.sections.size %]
6
  <p>[%- LxERP.t8("Neither sections nor function blocks have been created yet.") %]</p>
7

  
8
 [%- ELSE %]
9

  
10
  [%- SET at_least_one_function_block = 0 %]
11

  
12
  <form method="post" id="edit_time_cost_estimate_form">
13
   [%- L.hidden_tag('id', SELF.requirement_spec.id, id=id_prefix _ '_id') -%]
14

  
15
   [%# time-cost-estimate-context-menu %]
16
   <table class="time-cost-estimate">
17
    <tbody>
18
     [%- FOREACH section = SELF.requirement_spec.sections %]
19
      <tr class="listheading">
20
       <th>[%- LxERP.t8("Function block") %]</th>
21
       <th>[%- LxERP.t8("Complexity") %]</th>
22
       <th>[%- LxERP.t8("Risk") %]</th>
23
       <th align="right">[%- LxERP.t8("Time estimate") %]</th>
24
      </tr>
25

  
26
      <tr class="listrow section">
27
       <td colspan="5">[%- HTML.escape(section.fb_number) %]: [%- HTML.escape(section.title) %]</td>
28
      </tr>
29

  
30
      [%- IF section.children.size %]
31
       [%- SET at_least_one_function_block = 1 %]
32
       [%- FOREACH child = section.children %]
33
        [%- INCLUDE 'requirement_spec/_edit_time_and_cost_estimate_item.html'
34
                    id_prefix = id_prefix
35
                    item      = child
36
                    level     = 1 %]
37
       [%- END %]
38
      [%- END -%]
39
     [%- END -%]
40
    </tbody>
41
   </table>
42
  </form>
43
 [%- END %]
44
</div>
templates/webpages/requirement_spec/_edit_time_and_cost_estimate_item.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE P -%][%- USE L -%]
2
<tr class="listrow">
3
 [% L.hidden_tag("requirement_spec_items[+].id", item.id, id = id_prefix _ '_item_id') %]
4

  
5
 <td style="padding-left: [%- level * 50 -%]px">
6
  [%- P.simple_format(item.fb_number _ ": " _ item.description) -%]
7
 </td>
8
 <td>
9
  [%- L.select_tag('requirement_spec_items[].complexity_id', SELF.complexities, id=id_prefix _ '_complexity_id', title_key='description', default=item.complexity_id, style="width: 100%") %]<br>
10
 </td>
11
 <td>
12
  [%- L.select_tag('requirement_spec_items[].risk_id', SELF.risks, id=id_prefix _ '_risk_id', title_key='description', default=item.risk_id, style="width: 100%") %]<br>
13
 </td>
14
 [%- IF !item.children.size -%]
15
  <td align="right" nowrap>[%- P.man_days_tag('requirement_spec_items[].time_estimation', item, id=id_prefix _ '_time_estimation') %]</td>
16
 [%- ELSE -%]
17
  <td>&nbsp;</td>
18
 [%- END -%]
19
</tr>
20

  
21
[%- IF item.children.size -%]
22
 [%- FOREACH child = item.children -%]
23
  [%- INCLUDE 'requirement_spec/_edit_time_and_cost_estimate_item.html'
24
              id_prefix = id_prefix
25
              item      = child
26
              level     = level + 1 -%]
27
 [%- END -%]
28
[%- END -%]
templates/webpages/requirement_spec/_form.html
29 29

  
30 30
  <tr>
31 31
   <td>[% LxERP.t8("Hourly Rate") %]</td>
32
   <td>[% L.input_tag(form_prefix _ "hourly_rate_as_number", SELF.requirement_spec.hourly_rate_as_number, id=id_prefix _ '_hourly_rate_as_number') %]</td>
32
   <td>[% L.input_tag("requirement_spec.hourly_rate_as_number", SELF.requirement_spec.hourly_rate_as_number, id=id_prefix _ '_hourly_rate_as_number') %]</td>
33 33
  </tr>
34 34

  
35 35
 </table>
templates/webpages/requirement_spec/_show_time_and_cost_estimate.html
1
[%- USE LxERP -%][%- USE L -%][%- USE HTML -%][%- USE P -%]
2
[%- DEFAULT id_prefix = 'time_and_cost_estimate_form' %]
3

  
4
<div id="time_cost_estimate">
5
 [%- IF !SELF.requirement_spec.sections.size %]
6
  <p>[%- LxERP.t8("Neither sections nor function blocks have been created yet.") %]</p>
7

  
8
 [%- ELSE %]
9

  
10
  [%- SET at_least_one_function_block = 0 %]
11

  
12
  <table class="time-cost-estimate time-cost-estimate-context-menu">
13
   <tbody>
14
    [%- FOREACH section = SELF.requirement_spec.sections %]
15
     <tr class="listheading">
16
      <th>[%- LxERP.t8("Function block") %]</th>
17
      <th>[%- LxERP.t8("Complexity") %]</th>
18
      <th>[%- LxERP.t8("Risk") %]</th>
19
      <th align="right">[%- LxERP.t8("Time estimate") %]</th>
20
      <th align="right">[%- LxERP.t8("Cost") %]</th>
21
     </tr>
22

  
23
     <tr class="listrow section">
24
      <td colspan="5">[%- HTML.escape(section.fb_number) %]: [%- HTML.escape(section.title) %]</td>
25
     </tr>
26

  
27
     [%- IF section.children.size %]
28
      [%- SET at_least_one_function_block = 1 %]
29
      [%- FOREACH child = section.children %]
30
       [%- INCLUDE 'requirement_spec/_show_time_and_cost_estimate_item.html'
31
                   item  = child
32
                   level = 1 %]
33
      [%- END %]
34

  
35
      <tr class="listrow subtotal">
36
       <td style="padding-left: 50px" colspan="3" class="sum">[%- LxERP.t8("Sum for section") -%]:</td>
37
       <td align="right" nowrap>[%- P.format_man_days(section.time_estimation, 'skip_zero'=1) -%]</td>
38
       <td align="right" nowrap>[%- LxERP.format_amount(section.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
39
      </tr>
40
     [%- END -%]
41
    [%- END -%]
42
   </tbody>
43

  
44
   <tfoot>
45
    <tr>
46
     <td colspan="3">[%- LxERP.t8("Sum for #1", SELF.requirement_spec.type.description) -%]:</td>
47
     <td align="right" nowrap>[%- P.format_man_days(SELF.requirement_spec.time_estimation) -%]</td>
48
     <td align="right" nowrap>[%- LxERP.format_amount(SELF.requirement_spec.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
49
    </tr>
50
   </tfoot>
51
  </table>
52
 [%- END %]
53
</div>
templates/webpages/requirement_spec/_show_time_and_cost_estimate_item.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE P -%]
2
<tr class="listrow">
3
 <td style="padding-left: [%- level * 50 -%]px">
4
  [%- P.simple_format(item.fb_number _ ": " _ item.description) -%]
5
 </td>
6
 <td>[%- HTML.escape(item.complexity.description) -%]</td>
7
 <td>[%- HTML.escape(item.risk.description) -%]</td>
8
 [%- IF !item.children.size -%]
9
  <td align="right" nowrap>[%- P.format_man_days(item.time_estimation, skip_zero=1) -%]</td>
10
  <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
11
 [%- ELSE -%]
12
  <td>&nbsp;</td>
13
  <td>&nbsp;</td>
14
 [%- END -%]
15
</tr>
16

  
17
[%- IF item.children.size -%]
18
 [%- FOREACH child = item.children -%]
19
  [%- INCLUDE 'requirement_spec/_show_time_and_cost_estimate_item.html'
20
              item  = child
21
              level = level + 1 -%]
22
 [%- END -%]
23

  
24
 <tr class="listrow subtotal">
25
  <td style="padding-left: [%- (level + 1) * 50 -%]px" colspan="3">[%- LxERP.t8("Sum for #1", item.fb_number) -%]:</td>
26
  <td align="right" nowrap>[%- P.format_man_days(item.time_estimation, skip_zero=1) -%]</td>
27
  <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
28
 </tr>
29
[%- END -%]
templates/webpages/requirement_spec/show.html
10 10
 <ul>
11 11
  <li><a href="#function-blocks-tab">[%- LxERP.t8("Content") %]</a></li>
12 12
  <li><a href="controller.pl?action=RequirementSpec/ajax_edit&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Basic settings") %]</a></li>
13
  <li><a href="controller.pl?action=RequirementSpec/ajax_show_time_and_cost_estimate&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Time and cost estimate") %]</a></li>
13 14
 </ul>
14 15

  
15 16
 <div id="function-blocks-tab">

Auch abrufbar als: Unified diff