Steuerzonen überarbeitet - Prüfung und Löschen
authorG. Richardson <information@kivitendo-premium.de>
Thu, 30 Jul 2015 04:36:03 +0000 (06:36 +0200)
committerG. Richardson <information@kivitendo-premium.de>
Thu, 30 Jul 2015 10:43:54 +0000 (12:43 +0200)
Nicht benutzte Steuerzonen können jetzt gelöscht werden, sowie deren
Kontenzuordnungen geändert werden (wie bei Buchungsgruppen). Siehe
Feature #70.

Schlägt die Speicherung neuer Steuerzonen fehl, weil z.B. die
Buchungsgruppenkonten fehlen, gibt es nun einen Rollback und eine
ordentliche Fehlermeldung, siehe Fehler #68.

SL/Controller/Taxzones.pm
SL/DB/TaxZone.pm
locale/de/all
templates/webpages/taxzones/form.html

index 6f5adf7..60b820d 100644 (file)
@@ -4,8 +4,6 @@ use strict;
 
 use parent qw(SL::Controller::Base);
 
-#use List::Util qw(first);
-
 use SL::DB::TaxZone;
 use SL::Helper::Flash;
 use SL::Locale::String;
@@ -18,7 +16,7 @@ use Rose::Object::MakeMethods::Generic (
 );
 
 __PACKAGE__->run_before('check_auth');
-__PACKAGE__->run_before('load_config', only => [ qw(edit update) ]); #destroy
+__PACKAGE__->run_before('load_config', only => [ qw(edit update delete) ]);
 
 #
 # actions
@@ -29,7 +27,6 @@ sub action_list {
 
   my $taxzones = SL::DB::Manager::TaxZone->get_all_sorted();
 
-  $::form->header;
   $self->render('taxzones/list',
                 title    => t8('List of tax zones'),
                 TAXZONES => $taxzones);
@@ -69,6 +66,24 @@ sub action_update {
   $self->create_or_update;
 }
 
+sub action_delete {
+  my ($self) = @_;
+
+  # allow deletion of unused tax zones. Will fail, due to database
+  # constraints, if tax zone is used anywhere
+
+  my $db = $self->{config}->db;
+  $db->do_transaction(sub {
+        my $taxzone_charts = SL::DB::Manager::TaxzoneChart->get_all(where => [ taxzone_id => $self->config->id ]);
+        foreach my $taxzonechart ( @{$taxzone_charts} ) { $taxzonechart->delete };
+        $self->config->delete();
+        flash_later('info',  $::locale->text('The tax zone has been deleted.'));
+  }) || flash_later('error', $::locale->text('The tax zone is in use and cannot be deleted.'));
+
+  $self->redirect_to(action => 'list');
+
+}
+
 sub action_reorder {
   my ($self) = @_;
 
@@ -100,35 +115,54 @@ sub create_or_update {
   my $is_new = !$self->config->id;
 
   my $params = delete($::form->{config}) || { };
+
   delete $params->{id};
 
-  $self->config->assign_attributes(%{ $params });
+  my @errors;
+
+  my $db = $self->config->db;
+  $db->do_transaction( sub {
+
+    # always allow editing of description and obsolete
+    $self->config->assign_attributes( %{$params} ) ;
+
+    push(@errors, $self->config->validate); # check for description
+
+    if (@errors) {
+      die @errors . "\n";
+    };
 
-  my @errors = $self->config->validate;
+    $self->config->save;
 
-  if (@errors) {
-    flash('error', @errors);
-    $self->show_form(title => $is_new ? t8('Add taxzone') : t8('Edit taxzone'));
-    return;
-  }
+    if ( $is_new or $self->config->orphaned ) {
+      # Save taxzone_charts
+      my $buchungsgruppen = SL::DB::Manager::Buchungsgruppe->get_all_sorted();
 
-  $self->config->save;
-  $self->config->obsolete($::form->{"obsolete"});
+      foreach my $bg (@{ $buchungsgruppen }) {
+        my $income_accno_id  = $::form->{"income_accno_id_"  . $bg->id};
+        my $expense_accno_id = $::form->{"expense_accno_id_" . $bg->id};
 
-  #Save taxzone_charts for new taxzones:
-  if ($is_new) {
-    my $buchungsgruppen = SL::DB::Manager::Buchungsgruppe->get_all_sorted();
+        my ($income_accno, $expense_accno);
+        $income_accno  = SL::DB::Manager::Chart->find_by( id => $income_accno_id )  if $income_accno_id;
+        $expense_accno = SL::DB::Manager::Chart->find_by( id => $expense_accno_id ) if $expense_accno_id;
 
-    foreach my $bg (@{ $buchungsgruppen }) {
-      my $taxzone_chart = SL::DB::Manager::TaxzoneChart->find_by_or_create(buchungsgruppen_id => $bg->id, taxzone_id => $self->config->id);
+        push(@errors, t8('Buchungsgruppe #1 needs a valid income account' , $bg->description)) unless $income_accno;
+        push(@errors, t8('Buchungsgruppe #1 needs a valid expense account', $bg->description)) unless $expense_accno;
 
-      $taxzone_chart->taxzone_id($self->config->id);
-      $taxzone_chart->buchungsgruppen_id($bg->id);
-      $taxzone_chart->income_accno_id($::form->{"income_accno_id_" . $bg->id});
-      $taxzone_chart->expense_accno_id($::form->{"expense_accno_id_" . $bg->id});
-      $taxzone_chart->save;
+        my $taxzone_chart = SL::DB::Manager::TaxzoneChart->find_by_or_create(buchungsgruppen_id => $bg->id, taxzone_id => $self->config->id);
+        # if taxzonechart doesn't exist an empty new TaxzoneChart object is
+        # created by find_by_or_create, so we have to assign buchungsgruppe and
+        # taxzone again for the new case to work
+        $taxzone_chart->taxzone_id($self->config->id);
+        $taxzone_chart->buchungsgruppen_id($bg->id);
+        $taxzone_chart->income_accno_id($income_accno->id);
+        $taxzone_chart->expense_accno_id($expense_accno->id);
+        $taxzone_chart->save;
+      }
     }
-  }
+  } ) || die @errors ? join("\n", @errors) . "\n" : $db->error . "\n";
+         # die with rollback of taxzone save if saving of any of the taxzone_charts fails
+         # only show the $db->error if we haven't already identified the likely error ourselves
 
   flash_later('info', $is_new ? t8('The taxzone has been created.') : t8('The taxzone has been saved.'));
   $self->redirect_to(action => 'list');
index ad96ee2..6545d08 100644 (file)
@@ -23,4 +23,18 @@ sub validate {
   return @errors;
 }
 
+sub orphaned {
+  my ($self) = @_;
+  die 'not an accessor' if @_ > 1;
+
+  my @classes = qw(Customer Vendor Invoice Order DeliveryOrder PurchaseInvoice);
+  foreach my $class ( @classes ) {
+    my $module = 'SL::DB::' . $class;
+    eval "require $module";
+    my $manager = 'SL::DB::Manager::' . $class;
+    return 0 if $manager->get_all_count( query  => [ taxzone_id => $self->id ] );
+  };
+  return 1;
+}
+
 1;
index 2c0905f..50018d8 100755 (executable)
@@ -416,6 +416,8 @@ $self->{texts} = {
   'Break up the update and contact a service provider.' => 'Diese Option bricht das Update ab. Bitte kontaktieren Sie Ihren Administrator oder beauftragen einen Dienstleister.',
   'Buchungsdatum'               => 'Buchungsdatum',
   'Buchungsgruppe'              => 'Buchungsgruppe',
+  'Buchungsgruppe #1 needs a valid expense account' => 'Buchungsgruppe #1 braucht ein gültiges Aufwandskonto',
+  'Buchungsgruppe #1 needs a valid income account' => 'Buchungsgruppe #1 braucht ein gültiges Erfolgskonto',
   'Buchungsgruppe (database ID)' => 'Buchungsgruppe (Datenbank-ID)',
   'Buchungsgruppe (name)'       => 'Buchungsgruppe (Name)',
   'Buchungsgruppen'             => 'Buchungsgruppen',
@@ -2807,6 +2809,8 @@ $self->{texts} = {
   'The task server is not running.' => 'Der Task-Server läuft nicht.',
   'The task server was started successfully.' => 'Der Task-Server wurde erfolgreich gestartet.',
   'The task server was stopped successfully.' => 'Der Task-Server wurde erfolgreich beendet.',
+  'The tax zone has been deleted.' => 'Die Steuerzone wurde gelöscht.',
+  'The tax zone is in use and cannot be deleted.' => 'Die Steuerzone wird benutzt und kann nicht gelöscht werden',
   'The taxzone has been created.' => 'Die Steuerzone wurde erstellt.',
   'The taxzone has been saved.' => 'Die Steuerzone wurde gespeichert.',
   'The third way is to download the module from the above mentioned URL and to install the module manually following the installations instructions contained in the source archive.' => 'Die dritte Variante besteht darin, das Paket von der oben genannten URL herunterzuladen und es manuell zu installieren. Beachten Sie dabei die im Paket enthaltenen Installationsanweisungen.',
index bce8371..b127399 100644 (file)
 [%- FOREACH bg = BUCHUNGSGRUPPEN %]
   <tr>
     <th align="right">[% 'Revenue' | $T8 %] [% HTML.escape(bg.description) %]</th>
-    [%- IF SELF.config.id %]
-    <td>[% CHARTLIST.${bg.id}.income_accno %] -- [% CHARTLIST.${bg.id}.income_accno_description %]</td>
-    [%- ELSE %]
+    [%- IF NOT SELF.config.id %]
     <td>[% L.chart_picker('income_accno_id_' _ bg.id, SELF.defaults.income_accno_id, choose=1, type='IC_income,IC_sale', style=style) %]</td>
+    [%- ELSIF SELF.config.id AND SELF.config.orphaned %]
+    <td>[% L.chart_picker('income_accno_id_' _ bg.id, CHARTLIST.${bg.id}.income_accno_id, choose=1, type='IC_income,IC_sale', style=style) %]</td>
+    [%- ELSE %]
+    <td>[% CHARTLIST.${bg.id}.income_accno %] -- [% CHARTLIST.${bg.id}.income_accno_description %]</td>
     [%- END %]
   </tr>
   <tr>
     <th align="right">[% 'Expense' | $T8 %] [% HTML.escape(bg.description) %]</th>
-    [%- IF SELF.config.id %]
-    <td>[% CHARTLIST.${bg.id}.expense_accno %] -- [% CHARTLIST.${bg.id}.expense_accno_description %]</td>
-    [%- ELSE %]
+    [%- IF NOT SELF.config.id %]
     <td>[% L.chart_picker('expense_accno_id_' _ bg.id, SELF.defaults.expense_accno_id, choose=1, type='IC_expense,IC_cogs', style=style) %]</td>
+    [%- ELSIF SELF.config.id AND SELF.config.orphaned %]
+    <td>[% L.chart_picker('expense_accno_id_' _ bg.id, CHARTLIST.${bg.id}.expense_accno_id, choose=1, type='IC_expense,IC_cogs', style=style) %]</td>
+    [%- ELSE %]
+    <td>[% CHARTLIST.${bg.id}.expense_accno %] -- [% CHARTLIST.${bg.id}.expense_accno_description %]</td>
     [%- END %]
   </tr>
 [%- END %]
@@ -34,6 +38,9 @@
  <p>
   [% L.hidden_tag("action", "Taxzones/dispatch") %]
   [% L.submit_tag("action_" _  (SELF.config.id ? "update" : "create"), LxERP.t8('Save'), onclick="return check_prerequisites();") %]
+  [%- IF SELF.config.id AND SELF.config.orphaned %]
+    [% L.submit_tag("action_delete", LxERP.t8('Delete'), confirm=LxERP.t8('Are you sure?')) %]
+  [%- END %]
   <a href="[% SELF.url_for(action='list') %]">[%- LxERP.t8("Cancel") %]</a>
  </p>