Pflichtenhefte: Faktor für Verkaufspreis in Abschnitten & »Kostenschätzung« umbenannt
authorMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 7 Sep 2016 11:32:35 +0000 (13:32 +0200)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Mon, 7 Nov 2016 12:19:30 +0000 (13:19 +0100)
Aktuell haben wir nur einen Verkaufsbasispreis im Pflichtenheft: den
Stundensatz in den Grundeinstellungen. Dies ist allerdings der
Stundensatz, der Kunden gegenüber in Rechnung gestellt wird, und damit
ein Verkaufspreis und kein Kostenfaktor. Die Kosten anhand des
Verkaufspreises abzuschätzen ist aber unsinnig.

Daher ist es sinnvoller, erst mal von »Zeit- und Preisschätzung«
anstelle von »Zeit- und Kostenschätzung«.

Der neu eingeführte Faktor, der an Abschnitten angegeben werden kann,
ist dann ein Multiplikator für die Verkaufspreisschätzung. Er kann
z.B. benutzt werden, um geplante Wochenendarbeiten höher zu bepreisen.

Eine Einführung von echter Kostenschätzungen würde etwas mehr Arbeit
erfordern.

SL/Controller/RequirementSpecOrder.pm
SL/DB/MetaSetup/RequirementSpecItem.pm
locale/de/all
sql/Pg-upgrade2/requirement_spec_items_price_factor.sql [new file with mode: 0644]
templates/webpages/requirement_spec/_show_time_and_cost_estimate.html
templates/webpages/requirement_spec/_show_time_and_cost_estimate_item.html
templates/webpages/requirement_spec/show.html
templates/webpages/requirement_spec_item/_section_form.html
templates/webpages/requirement_spec_item/_section_header.html

index f695a51..a949901 100644 (file)
@@ -318,8 +318,8 @@ sub create_order_item {
     longdescription => $longdescription,
     qty             => $is_time_based ? $section->time_estimation * 1 : 1,
     unit            => $is_time_based ? $self->h_unit_name            : $part->unit,
-    sellprice       => $::form->round_amount($self->requirement_spec->hourly_rate * ($is_time_based ? 1 : $section->time_estimation), 2),
-    lastcost        => $part->lastcost,
+    sellprice       => $::form->round_amount($self->requirement_spec->hourly_rate * ($is_time_based ? 1 : $section->time_estimation * $section->sellprice_factor), 2),
+    lastcost        => $part->lastcost * $section->sellprice_factor,
     discount        => 0,
     project_id      => $self->requirement_spec->project_id,
   );
index 07bac07..50c167d 100644 (file)
@@ -24,6 +24,7 @@ __PACKAGE__->meta->columns(
   position             => { type => 'integer', not_null => 1 },
   requirement_spec_id  => { type => 'integer', not_null => 1 },
   risk_id              => { type => 'integer' },
+  sellprice_factor     => { type => 'numeric', default => 1, precision => 10, scale => 5 },
   time_estimation      => { type => 'numeric', default => '0', not_null => 1, precision => 12, scale => 2 },
   title                => { type => 'text' },
 );
index 56478f5..9347f38 100755 (executable)
@@ -624,7 +624,6 @@ $self->{texts} = {
   'Copy requirement spec'       => 'Pflichtenheft kopieren',
   'Copy template'               => 'Vorlage kopieren',
   'Correct taxkey'              => 'Richtiger Steuerschlüssel',
-  'Cost'                        => 'Kosten',
   'Costs'                       => 'Kosten',
   'Could not load class #1 (#2): "#3"' => 'Konnte Klasse #1 (#2) nicht laden: "#3"',
   'Could not load class #1, #2' => 'Konnte Klasse #1 nicht laden: "#2"',
@@ -3185,7 +3184,8 @@ $self->{texts} = {
   'This will set an exact price.' => 'Diese Option setzt einen festen Preis.',
   'Three Options:'              => 'Drei Optionen:',
   'Time Format'                 => 'Uhrzeitformat',
-  'Time and cost estimate'      => 'Zeit- und Kostenschätzung',
+  'Time Tracking'               => 'Zeiterfassung',
+  'Time and price estimate'     => 'Zeit- und Preisschätzung',
   'Time estimate'               => 'Zeitschätzung',
   'Time period for the analysis:' => 'Analysezeitraum:',
   'Time/cost estimate actions'  => 'Aktionen für Kosten-/Zeitabschätzung',
diff --git a/sql/Pg-upgrade2/requirement_spec_items_price_factor.sql b/sql/Pg-upgrade2/requirement_spec_items_price_factor.sql
new file mode 100644 (file)
index 0000000..b519b38
--- /dev/null
@@ -0,0 +1,9 @@
+-- @tag: requirement_spec_items_price_factor
+-- @description: Pflichtenheftabschnitte: Faktor für Verkaufspreis
+-- @depends: requirement_specs
+ALTER TABLE requirement_spec_items
+  ADD COLUMN   sellprice_factor NUMERIC(10, 5),
+  ALTER COLUMN sellprice_factor SET DEFAULT 1;
+
+UPDATE requirement_spec_items
+SET sellprice_factor = 1;
index e8e4c3d..2cff5d9 100644 (file)
@@ -1,5 +1,6 @@
 [%- USE LxERP -%][%- USE L -%][%- USE HTML -%][%- USE P -%]
 [%- DEFAULT id_prefix = 'time_and_cost_estimate_form' %]
+[%- SET total_cost = 0 %]
 
 <div id="time_cost_estimate"[% IF initially_hidden %] style="display: none;"[% END %]>
  [%- IF !SELF.requirement_spec.sections.size %]
@@ -18,7 +19,7 @@
       <th>[%- LxERP.t8("Risk") %]</th>
       <th align="right">[%- LxERP.t8("Time estimate") %]</th>
       [%- UNLESS SELF.requirement_spec.is_template %]
-       <th align="right">[%- LxERP.t8("Cost") %]</th>
+       <th align="right">[%- LxERP.t8("Price") %]</th>
       [%- END %]
      </tr>
 
       [%- SET at_least_one_function_block = 1 %]
       [%- FOREACH child = section.children_sorted %]
        [%- INCLUDE 'requirement_spec/_show_time_and_cost_estimate_item.html'
-                   item  = child
-                   level = 1 %]
+                   section = section
+                   item    = child
+                   level   = 1 %]
       [%- END %]
 
       <tr class="listrow subtotal">
        <td style="padding-left: 50px" colspan="3" class="sum">[%- LxERP.t8("Sum for section") -%]:</td>
        <td align="right" nowrap>[%- P.format_man_days(section.time_estimation, 'skip_zero'=1) -%]</td>
        [%- UNLESS SELF.requirement_spec.is_template %]
-        <td align="right" nowrap>[%- LxERP.format_amount(section.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
+        [%- SET section_cost = section.time_estimation * SELF.requirement_spec.hourly_rate * section.sellprice_factor;
+                total_cost   = total_cost + section_cost %]
+        <td align="right" nowrap>[%- LxERP.format_amount(section_cost, 2) -%] EUR</td>
        [%- END %]
       </tr>
      [%- END -%]
@@ -50,7 +54,7 @@
      <td colspan="3">[%- LxERP.t8("Sum for #1", SELF.requirement_spec.type.description) -%]:</td>
      <td align="right" nowrap>[%- P.format_man_days(SELF.requirement_spec.time_estimation) -%]</td>
      [%- UNLESS SELF.requirement_spec.is_template %]
-      <td align="right" nowrap>[%- LxERP.format_amount(SELF.requirement_spec.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
+      <td align="right" nowrap>[%- LxERP.format_amount(total_cost, 2) -%] EUR</td>
      [%- END %]
     </tr>
    </tfoot>
index c3d21e3..d051972 100644 (file)
@@ -8,7 +8,7 @@
  [%- IF !item.children.size -%]
   <td align="right" nowrap>[%- P.format_man_days(item.time_estimation, skip_zero=1) -%]</td>
   [%- UNLESS SELF.requirement_spec.is_template %]
-   <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
+   <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate * section.sellprice_factor, 2) -%] EUR</td>
   [%- END %]
  [%- ELSE -%]
   <td>&nbsp;</td>
 [%- IF item.children.size -%]
  [%- FOREACH child = item.children_sorted -%]
   [%- INCLUDE 'requirement_spec/_show_time_and_cost_estimate_item.html'
-              item  = child
-              level = level + 1 -%]
+              section = section
+              item    = child
+              level   = level + 1 -%]
  [%- END -%]
 
  <tr class="listrow subtotal">
   <td style="padding-left: [%- (level + 1) * 50 -%]px" colspan="3">[%- LxERP.t8("Sum for #1", item.fb_number) -%]:</td>
   <td align="right" nowrap>[%- P.format_man_days(item.time_estimation, skip_zero=1) -%]</td>
   [%- UNLESS SELF.requirement_spec.is_template %]
-   <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate, 2) -%] EUR</td>
+   <td align="right" nowrap>[%- LxERP.format_amount(item.time_estimation * SELF.requirement_spec.hourly_rate * section.sellprice_factor, 2) -%] EUR</td>
   [%- END %]
  </tr>
 [%- END -%]
index 285dcee..c627a16 100644 (file)
@@ -11,7 +11,7 @@
  <ul>
   <li id="tab-header-function-block"><a href="#function-blocks-tab">[%- LxERP.t8("Content") %]</a></li>
   <li id="tab-header-basic-settings"><a href="controller.pl?action=RequirementSpec/ajax_show_basic_settings&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Basic settings") %]</a></li>
-  <li id="tab-header-time-cost-estimate"><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>
+  <li id="tab-header-time-cost-estimate"><a href="controller.pl?action=RequirementSpec/ajax_show_time_and_cost_estimate&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Time and price estimate") %]</a></li>
   <li id="tab-header-additional-parts"><a href="controller.pl?action=RequirementSpecPart/show&requirement_spec_id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Additional articles") %]</a></li>
   [%- UNLESS SELF.requirement_spec.is_template %]
    <li id="tab-header-versions"><a href="controller.pl?action=RequirementSpecVersion/list&requirement_spec_id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Versions") %]</a></li>
index 93d75be..14c5af5 100644 (file)
   </p>
  [%- END %]
 
- <p>
-  [%- LxERP.t8("Description") %]:<br>
-  [% L.textarea_tag(id_base _ '.description_as_restricted_html', SELF.item.description_as_restricted_html, id=id_base _ '_description', rows=8, cols=80, style=style, class='texteditor') %]
- </p>
-
- <p>
-  [% L.ajax_submit_tag('controller.pl?action=RequirementSpecItem/ajax_' _ (SELF.item.id ? 'update' : 'create'), '#' _ id_base _ '_form', LxERP.t8('Save')) %]
-  <a href="#" onclick="kivi.requirement_spec.cancel_edit_item_form('[% id_base %]', { to_show: '[% hidden %]' })">[%- LxERP.t8("Cancel") %]</a>
- </p>
+ <table border="0">
+  <tr valign="top">
+   <td>
+    [%- LxERP.t8("Description") %]:<br>
+    [% L.textarea_tag(id_base _ '.description_as_restricted_html', SELF.item.description_as_restricted_html, id=id_base _ '_description', rows=8, cols=80, style=style, class='texteditor') %]<br>
+    [% L.ajax_submit_tag('controller.pl?action=RequirementSpecItem/ajax_' _ (SELF.item.id ? 'update' : 'create'), '#' _ id_base _ '_form', LxERP.t8('Save')) %]
+    <a href="#" onclick="kivi.requirement_spec.cancel_edit_item_form('[% id_base %]', { to_show: '[% hidden %]' })">[%- LxERP.t8("Cancel") %]</a>
+   </td>
+
+   <td>
+   [% LxERP.t8("Price Factor") %]:<br>
+   [% L.input_tag(id_base _ ".sellprice_factor_as_number", SELF.item.sellprice_factor_as_number, size="6") %]<br>
+   </td>
+  </tr>
+ </table>
 
 [%- IF SELF.predefined_texts.size %]
  <script type="text/javascript">
index 4df9892..4c4743f 100644 (file)
@@ -18,3 +18,7 @@
   <span class="dimmed-text">[%- LxERP.t8("No text has been entered yet.") %]</span>
  [%- END %]
 </div>
+
+<div class="smaller gray" style="text-align:right">
+ [%- LxERP.t8("Price Factor") -%]: [%- LxERP.format_amount(requirement_spec_item.sellprice_factor, -2) -%]
+</div>