Merge pull request #36 from kivitendo/master-partial_invoice-2
authorJan Büren <jan@kivitendo-premium.de>
Mon, 14 Feb 2022 13:00:23 +0000 (14:00 +0100)
committerGitHub <noreply@github.com>
Mon, 14 Feb 2022 13:00:23 +0000 (14:00 +0100)
Master partial invoice 2

36 files changed:
SL/AR.pm
SL/BackgroundJob/SelfTest/Transactions.pm
SL/Controller/File.pm
SL/Controller/Order.pm
SL/Controller/PriceSource.pm
SL/DB/Helper/Payment.pm
SL/DB/Invoice.pm
SL/DB/MetaSetup/Default.pm
SL/File/Backend/Webdav.pm
SL/Form.pm
SL/Helper/PrintOptions.pm
SL/IC.pm
SL/IS.pm
SL/TransNumber.pm
SL/Webdav.pm
bin/mozilla/ar.pl
bin/mozilla/io.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
locale/de/all
locale/en/all
menus/user/20-invoice-for-advance-payment.yaml [new file with mode: 0644]
sql/Pg-upgrade2/defaults_advance_payment_clearing_chart_id.sql [new file with mode: 0644]
sql/Pg-upgrade2/defaults_advance_payment_transfer_charts.sql [new file with mode: 0644]
sql/Pg-upgrade2/file_storage_partial_invoices.sql [new file with mode: 0644]
sql/Pg-upgrade2/new_chart_1593_1495.sql [new file with mode: 0644]
sql/Pg-upgrade2/new_chart_3260_1711.sql [new file with mode: 0644]
sql/Pg-upgrade2/new_chart_3272_1718.sql [new file with mode: 0644]
templates/print/marei/deutsch.tex
templates/print/marei/english.tex
templates/print/marei/final_invoice.tex [new symlink]
templates/print/marei/invoice.tex
templates/print/marei/invoice_for_advance_payment.tex [new symlink]
templates/webpages/client_config/_default_accounts.html
templates/webpages/is/form_footer.html
templates/webpages/is/form_header.html

index 280184d..77c62d3 100644 (file)
--- a/SL/AR.pm
+++ b/SL/AR.pm
@@ -487,6 +487,7 @@ sub ar_transactions {
     qq|  a.shippingpoint, a.storno, a.storno_id, a.globalproject_id, | .
     qq|  a.marge_total, a.marge_percent, | .
     qq|  a.transaction_description, a.direct_debit, | .
+    qq|  a.type, | .
     qq|  pr.projectnumber AS globalprojectnumber, | .
     qq|  c.name, c.customernumber, c.country, c.ustid, b.description as customertype, | .
     qq|  c.id as customer_id, | .
index 9eda722..ab145ce 100644 (file)
@@ -472,6 +472,7 @@ sub check_ar_acc_trans_amount {
           from acc_trans ac left join ar on (ac.trans_id = ar.id)
           WHERE ac.chart_link like 'AR_amount%'
           AND ac.transdate >= ? AND ac.transdate <= ?
+          AND ar.type       = 'invoice'
           group by invnumber,netamount having sum(ac.amount) <> ar.netamount|;
 
   my $ar_amount_not_ac_amount = selectall_hashref_query($::form, $self->dbh, $query, $self->fromdate, $self->todate);
index 7cd9406..dcf790e 100644 (file)
@@ -55,25 +55,27 @@ __PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete aja
 # model:  base name of the rose model
 # right:  access right used for import
 my %file_types = (
-  'sales_quotation'         => { gen => 1, gltype => '',   dir =>'SalesQuotation',       model => 'Order',          right => 'import_ar'  },
-  'sales_order'             => { gen => 5, gltype => '',   dir =>'SalesOrder',           model => 'Order',          right => 'import_ar'  },
-  'sales_delivery_order'    => { gen => 1, gltype => '',   dir =>'SalesDeliveryOrder',   model => 'DeliveryOrder',  right => 'import_ar'  },
-  'invoice'                 => { gen => 1, gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
-  'credit_note'             => { gen => 1, gltype => '',   dir =>'CreditNote',           model => 'Invoice',        right => 'import_ar'  },
-  'request_quotation'       => { gen => 7, gltype => '',   dir =>'RequestForQuotation',  model => 'Order',          right => 'import_ap'  },
-  'purchase_order'          => { gen => 7, gltype => '',   dir =>'PurchaseOrder',        model => 'Order',          right => 'import_ap'  },
-  'purchase_delivery_order' => { gen => 7, gltype => '',   dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder',  right => 'import_ap'  },
-  'purchase_invoice'        => { gen => 6, gltype => 'ap', dir =>'PurchaseInvoice',      model => 'PurchaseInvoice',right => 'import_ap'  },
-  'vendor'                  => { gen => 0, gltype => '',   dir =>'Vendor',               model => 'Vendor',         right => 'xx'         },
-  'customer'                => { gen => 1, gltype => '',   dir =>'Customer',             model => 'Customer',       right => 'xx'         },
-  'project'                 => { gen => 0, gltype => '',   dir =>'Project',              model => 'Project',        right => 'xx'         },
-  'part'                    => { gen => 0, gltype => '',   dir =>'Part',                 model => 'Part',           right => 'xx'         },
-  'gl_transaction'          => { gen => 6, gltype => 'gl', dir =>'GeneralLedger',        model => 'GLTransaction',  right => 'import_ap'  },
-  'draft'                   => { gen => 0, gltype => '',   dir =>'Draft',                model => 'Draft',          right => 'xx'         },
-  'csv_customer'            => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Customer',       right => 'xx'         },
-  'csv_vendor'              => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Vendor',         right => 'xx'         },
-  'shop_image'              => { gen => 0, gltype => '',   dir =>'ShopImages',           model => 'Part',           right => 'xx'         },
-  'letter'                  => { gen => 7, gltype => '',   dir =>'Letter',               model => 'Letter',         right => 'sales_letter_edit | purchase_letter_edit' },
+  'sales_quotation'             => { gen => 1, gltype => '',   dir =>'SalesQuotation',       model => 'Order',          right => 'import_ar'  },
+  'sales_order'                 => { gen => 5, gltype => '',   dir =>'SalesOrder',           model => 'Order',          right => 'import_ar'  },
+  'sales_delivery_order'        => { gen => 1, gltype => '',   dir =>'SalesDeliveryOrder',   model => 'DeliveryOrder',  right => 'import_ar'  },
+  'invoice'                     => { gen => 1, gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
+  'invoice_for_advance_payment' => { gen => 1, gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
+  'final_invoice'               => { gen => 1, gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
+  'credit_note'                 => { gen => 1, gltype => '',   dir =>'CreditNote',           model => 'Invoice',        right => 'import_ar'  },
+  'request_quotation'           => { gen => 7, gltype => '',   dir =>'RequestForQuotation',  model => 'Order',          right => 'import_ap'  },
+  'purchase_order'              => { gen => 7, gltype => '',   dir =>'PurchaseOrder',        model => 'Order',          right => 'import_ap'  },
+  'purchase_delivery_order'     => { gen => 7, gltype => '',   dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder',  right => 'import_ap'  },
+  'purchase_invoice'            => { gen => 6, gltype => 'ap', dir =>'PurchaseInvoice',      model => 'PurchaseInvoice',right => 'import_ap'  },
+  'vendor'                      => { gen => 0, gltype => '',   dir =>'Vendor',               model => 'Vendor',         right => 'xx'         },
+  'customer'                    => { gen => 1, gltype => '',   dir =>'Customer',             model => 'Customer',       right => 'xx'         },
+  'project'                     => { gen => 0, gltype => '',   dir =>'Project',              model => 'Project',        right => 'xx'         },
+  'part'                        => { gen => 0, gltype => '',   dir =>'Part',                 model => 'Part',           right => 'xx'         },
+  'gl_transaction'              => { gen => 6, gltype => 'gl', dir =>'GeneralLedger',        model => 'GLTransaction',  right => 'import_ap'  },
+  'draft'                       => { gen => 0, gltype => '',   dir =>'Draft',                model => 'Draft',          right => 'xx'         },
+  'csv_customer'                => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Customer',       right => 'xx'         },
+  'csv_vendor'                  => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Vendor',         right => 'xx'         },
+  'shop_image'                  => { gen => 0, gltype => '',   dir =>'ShopImages',           model => 'Part',           right => 'xx'         },
+  'letter'                      => { gen => 7, gltype => '',   dir =>'Letter',               model => 'Letter',         right => 'sales_letter_edit | purchase_letter_edit' },
 );
 
 #--- 4 locale ---#
index 64ad14d..b4328a1 100644 (file)
@@ -57,11 +57,11 @@ use Rose::Object::MakeMethods::Generic
 __PACKAGE__->run_before('check_auth');
 
 __PACKAGE__->run_before('recalc',
-                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_ap_transaction
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_invoice_for_advance_payment save_and_final_invoice save_and_ap_transaction
                                      print send_email) ]);
 
 __PACKAGE__->run_before('get_unalterable_data',
-                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_ap_transaction
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_invoice_for_advance_payment save_and_final_invoice save_and_ap_transaction
                                      print send_email) ]);
 
 #
@@ -688,6 +688,26 @@ sub action_save_and_invoice {
   );
 }
 
+sub action_save_and_invoice_for_advance_payment {
+  my ($self) = @_;
+
+  $self->save_and_redirect_to(
+    controller       => 'oe.pl',
+    action           => 'oe_invoice_from_order',
+    new_invoice_type => 'invoice_for_advance_payment',
+  );
+}
+
+sub action_save_and_final_invoice {
+  my ($self) = @_;
+
+  $self->save_and_redirect_to(
+    controller       => 'oe.pl',
+    action           => 'oe_invoice_from_order',
+    new_invoice_type => 'final_invoice',
+  );
+}
+
 # workflow from sales order to sales quotation
 sub action_sales_quotation {
   $_[0]->workflow_sales_or_request_for_quotation();
@@ -1974,6 +1994,18 @@ sub setup_edit_action_bar {
   my @req_trans_cost_art = qw(kivi.Order.check_transport_cost_article_presence) x!!$::instance_conf->get_transport_cost_reminder_article_number_id;
   my @req_cusordnumber   = qw(kivi.Order.check_cusordnumber_presence)           x($self->type eq sales_order_type() && $::instance_conf->get_order_warn_no_cusordnumber);
 
+  my $has_invoice_for_advance_payment;
+  if ($self->order->id && $self->type eq sales_order_type()) {
+    my $lr = $self->order->linked_records(direction => 'to', to => ['Invoice']);
+    $has_invoice_for_advance_payment = any {'SL::DB::Invoice' eq ref $_ && "invoice_for_advance_payment" eq $_->type} @$lr;
+  }
+
+  my $has_final_invoice;
+  if ($self->order->id && $self->type eq sales_order_type()) {
+    my $lr = $self->order->linked_records(direction => 'to', to => ['Invoice']);
+    $has_final_invoice               = any {'SL::DB::Invoice' eq ref $_ && "final_invoice" eq $_->type} @$lr;
+  }
+
   for my $bar ($::request->layout->get('actionbar')) {
     $bar->add(
       combobox => [
@@ -2040,6 +2072,26 @@ sub setup_edit_action_bar {
                          @req_trans_cost_art, @req_cusordnumber,
           ],
         ],
+        action => [
+          ($has_invoice_for_advance_payment ? t8('Save and Further Invoice for Advance Payment') : t8('Save and Invoice for Advance Payment')),
+          call      => [ 'kivi.Order.save', 'save_and_invoice_for_advance_payment', $::instance_conf->get_order_warn_duplicate_parts ],
+          checks    => [ 'kivi.Order.check_save_active_periodic_invoices',
+                         @req_trans_cost_art, @req_cusordnumber,
+          ],
+          disabled  => $has_final_invoice ? t8('This order has already a final invoice.')
+                                          : undef,
+          only_if   => (any { $self->type eq $_ } (sales_order_type())),
+        ],
+        action => [
+          t8('Save and Final Invoice'),
+          call      => [ 'kivi.Order.save', 'save_and_final_invoice', $::instance_conf->get_order_warn_duplicate_parts ],
+          checks    => [ 'kivi.Order.check_save_active_periodic_invoices',
+                         @req_trans_cost_art, @req_cusordnumber,
+          ],
+          disabled  => $has_final_invoice ? t8('This order has already a final invoice.')
+                                          : undef,
+          only_if   => (any { $self->type eq $_ } (sales_order_type())) && $has_invoice_for_advance_payment,
+        ],
         action => [
           t8('Save and AP Transaction'),
           call      => [ 'kivi.Order.save', 'save_and_ap_transaction', $::instance_conf->get_order_warn_duplicate_parts ],
index 85e3765..c0c113c 100644 (file)
@@ -83,6 +83,8 @@ sub _make_record_item {
     sales_quotation         => 'OrderItem',
     request_quotation       => 'OrderItem',
     invoice                 => 'InvoiceItem',
+    invoice_for_advance_payment => 'InvoiceItem',
+    final_invoice           => 'InvoiceItem',
     purchase_invoice        => 'InvoiceItem',
     credit_note             => 'InvoiceItem',
     purchase_delivery_order => 'DeliveryOrderItem',
index 6dee091..8d0174d 100644 (file)
@@ -77,6 +77,7 @@ sub pay_invoice {
   };
 
   # currency is either passed or use the invoice currency if it differs from the default currency
+  # TODO remove
   my ($exchangerate,$currency);
   if ($params{currency} || $params{currency_id} || $self->currency_id != $::instance_conf->get_currency_id) {
     if ($params{currency} || $params{currency_id} ) { # currency was specified
@@ -290,6 +291,67 @@ sub pay_invoice {
     $arap_booking->save;
     push @new_acc_ids, $arap_booking->acc_trans_id;
 
+    # hook for invoice_for_advance_payment DATEV always pairs, acc_trans_id has to be higher than arap_booking ;-)
+    if ($self->invoice_type eq 'invoice_for_advance_payment') {
+      my $clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load;
+      die "No Clearing Chart for Advance Payment" unless ref $clearing_chart eq 'SL::DB::Chart';
+
+      # what does ptc say
+      my %inv_calc = $self->calculate_prices_and_taxes();
+      my @trans_ids = keys %{ $inv_calc{amounts} };
+      die "Invalid state for advance payment more than one trans_id" if (scalar @trans_ids > 1);
+      my $entry = delete $inv_calc{amounts}{$trans_ids[0]};
+      my $tax;
+      if ($entry->{tax_id}) {
+        $tax = SL::DB::Manager::Tax->find_by(id => $entry->{tax_id}); # || die "Can't find tax with id " . $entry->{tax_id};
+      }
+      if ($tax and $tax->rate != 0) {
+        my ($netamount, $taxamount);
+        my $roundplaces = 2;
+        # we dont have a clue about skonto, that's why we use $arap_amount as taxincluded
+        ($netamount, $taxamount) = Form->calculate_tax($arap_amount, $tax->rate, 1, $roundplaces);
+        # for debugging database set
+        my $fullmatch = $netamount == $entry->{amount} ? '::netamount total true' : '';
+        my $transfer_chart = $tax->taxkey == 2 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_7_id)->load
+                          :  $tax->taxkey == 3 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_19_id)->load
+                          :  undef;
+        die "No Transfer Chart for Advance Payment" unless ref $transfer_chart eq 'SL::DB::Chart';
+
+        my $arap_full_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                           chart_id   => $clearing_chart->id,
+                                                           chart_link => $clearing_chart->link,
+                                                           amount     => $arap_amount * -1, # full amount
+                                                           transdate  => $transdate_obj,
+                                                           source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                           taxkey     => 0,
+                                                           tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
+        $arap_full_booking->save;
+        push @new_acc_ids, $arap_full_booking->acc_trans_id;
+
+        my $arap_tax_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                          chart_id   => $transfer_chart->id,
+                                                          chart_link => $transfer_chart->link,
+                                                          amount     => _round($netamount), # full amount
+                                                          transdate  => $transdate_obj,
+                                                          source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                          taxkey     => $tax->taxkey,
+                                                          tax_id     => $tax->id);
+        $arap_tax_booking->save;
+        push @new_acc_ids, $arap_tax_booking->acc_trans_id;
+
+        my $tax_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                     chart_id   => $tax->chart_id,
+                                                     chart_link => $tax->chart->link,
+                                                     amount     => _round($taxamount),
+                                                     transdate  => $transdate_obj,
+                                                     source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                     taxkey     => 0,
+                                                     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
+
+        $tax_booking->save;
+        push @new_acc_ids, $tax_booking->acc_trans_id;
+      }
+    }
     $fx_gain_loss_amount *= -1 if $self->is_sales;
     $self->paid($self->paid + _round($paid_amount) + $fx_gain_loss_amount) if $paid_amount;
     $self->datepaid($transdate_obj);
index 54f5483..45259a0 100644 (file)
@@ -539,6 +539,9 @@ sub invoice_type {
   my ($self) = @_;
 
   return 'ar_transaction'     if !$self->invoice;
+  return 'invoice_for_advance_payment_storno' if $self->type eq 'invoice_for_advance_payment' && $self->amount < 0 &&  $self->storno;
+  return 'invoice_for_advance_payment'        if $self->type eq 'invoice_for_advance_payment';
+  return 'final_invoice'                      if $self->type eq 'final_invoice';
   # stornoed credit_notes are still credit notes and not invoices
   return 'credit_note'        if $self->type eq 'credit_note' && $self->amount < 0;
   return 'invoice_storno'     if $self->type ne 'credit_note' && $self->amount < 0 &&  $self->storno;
@@ -559,6 +562,9 @@ sub displayable_type {
   return t8('Credit Note')                            if $self->invoice_type eq 'credit_note';
   return t8('Invoice') . "(" . t8('Storno') . ")"     if $self->invoice_type eq 'invoice_storno';
   return t8('Credit Note') . "(" . t8('Storno') . ")" if $self->invoice_type eq 'credit_note_storno';
+  return t8('Invoice for Advance Payment')            if $self->invoice_type eq 'invoice_for_advance_payment';
+  return t8('Invoice for Advance Payment') . "(" . t8('Storno') . ")" if $self->invoice_type eq 'invoice_for_advance_payment_storno';
+  return t8('Final Invoice')                          if $self->invoice_type eq 'final_invoice';
   return t8('Invoice');
 }
 
@@ -573,6 +579,9 @@ sub abbreviation {
   return t8('Credit note (one letter abbreviation)') if $self->invoice_type eq 'credit_note';
   return t8('Invoice (one letter abbreviation)') . "(" . t8('Storno (one letter abbreviation)') . ")" if $self->invoice_type eq 'invoice_storno';
   return t8('Credit note (one letter abbreviation)') . "(" . t8('Storno (one letter abbreviation)') . ")"  if $self->invoice_type eq 'credit_note_storno';
+  return t8('Invoice for Advance Payment (one letter abbreviation)')  if $self->invoice_type eq 'invoice_for_advance_payment';
+  return t8('Invoice for Advance Payment with Storno (abbreviation)') if $self->invoice_type eq 'invoice_for_advance_payment_storno';
+  return t8('Final Invoice (one letter abbreviation)')                if $self->invoice_type eq 'final_invoice';
   return t8('Invoice (one letter abbreviation)');
 }
 
index ff3733a..b7fe2b1 100644 (file)
@@ -15,6 +15,9 @@ __PACKAGE__->meta->columns(
   address_street1                           => { type => 'text' },
   address_street2                           => { type => 'text' },
   address_zipcode                           => { type => 'text' },
+  advance_payment_clearing_chart_id         => { type => 'integer' },
+  advance_payment_taxable_19_id             => { type => 'integer' },
+  advance_payment_taxable_7_id              => { type => 'integer' },
   allow_new_purchase_delivery_order         => { type => 'boolean', default => 'true', not_null => 1 },
   allow_new_purchase_invoice                => { type => 'boolean', default => 'true', not_null => 1 },
   allow_sales_invoice_from_sales_order      => { type => 'boolean', default => 'true', not_null => 1 },
index 2956e03..bf8f9af 100644 (file)
@@ -113,47 +113,51 @@ sub enabled {
 #
 
 my %type_to_path = (
-  sales_quotation         => 'angebote',
-  sales_order             => 'bestellungen',
-  request_quotation       => 'anfragen',
-  purchase_order          => 'lieferantenbestellungen',
-  sales_delivery_order    => 'verkaufslieferscheine',
-  purchase_delivery_order => 'einkaufslieferscheine',
-  credit_note             => 'gutschriften',
-  invoice                 => 'rechnungen',
-  purchase_invoice        => 'einkaufsrechnungen',
-  part                    => 'waren',
-  service                 => 'dienstleistungen',
-  assembly                => 'erzeugnisse',
-  letter                  => 'briefe',
-  general_ledger          => 'dialogbuchungen',
-  gl_transaction          => 'dialogbuchungen',
-  accounts_payable        => 'kreditorenbuchungen',
-  shop_image              => 'shopbilder',
-  customer                => 'kunden',
-  vendor                  => 'lieferanten',
+  sales_quotation             => 'angebote',
+  sales_order                 => 'bestellungen',
+  request_quotation           => 'anfragen',
+  purchase_order              => 'lieferantenbestellungen',
+  sales_delivery_order        => 'verkaufslieferscheine',
+  purchase_delivery_order     => 'einkaufslieferscheine',
+  credit_note                 => 'gutschriften',
+  invoice                     => 'rechnungen',
+  invoice_for_advance_payment => 'rechnungen',
+  final_invoice               => 'rechnungen',
+  purchase_invoice            => 'einkaufsrechnungen',
+  part                        => 'waren',
+  service                     => 'dienstleistungen',
+  assembly                    => 'erzeugnisse',
+  letter                      => 'briefe',
+  general_ledger              => 'dialogbuchungen',
+  gl_transaction              => 'dialogbuchungen',
+  accounts_payable            => 'kreditorenbuchungen',
+  shop_image                  => 'shopbilder',
+  customer                    => 'kunden',
+  vendor                      => 'lieferanten',
 );
 
 my %type_to_model = (
-  sales_quotation         => 'Order',
-  sales_order             => 'Order',
-  request_quotation       => 'Order',
-  purchase_order          => 'Order',
-  sales_delivery_order    => 'DeliveryOrder',
-  purchase_delivery_order => 'DeliveryOrder',
-  credit_note             => 'Invoice',
-  invoice                 => 'Invoice',
-  purchase_invoice        => 'PurchaseInvoice',
-  part                    => 'Part',
-  service                 => 'Part',
-  assembly                => 'Part',
-  letter                  => 'Letter',
-  general_ledger          => 'GLTransaction',
-  gl_transaction          => 'GLTransaction',
-  accounts_payable        => 'GLTransaction',
-  shop_image              => 'Part',
-  customer                => 'Customer',
-  vendor                  => 'Vendor',
+  sales_quotation             => 'Order',
+  sales_order                 => 'Order',
+  request_quotation           => 'Order',
+  purchase_order              => 'Order',
+  sales_delivery_order        => 'DeliveryOrder',
+  purchase_delivery_order     => 'DeliveryOrder',
+  credit_note                 => 'Invoice',
+  invoice                     => 'Invoice',
+  invoice_for_advance_payment => 'Invoice',
+  final_invoice               => 'Invoice',
+  purchase_invoice            => 'PurchaseInvoice',
+  part                        => 'Part',
+  service                     => 'Part',
+  assembly                    => 'Part',
+  letter                      => 'Letter',
+  general_ledger              => 'GLTransaction',
+  gl_transaction              => 'GLTransaction',
+  accounts_payable            => 'GLTransaction',
+  shop_image                  => 'Part',
+  customer                    => 'Customer',
+  vendor                      => 'Vendor',
 );
 
 my %model_to_number = (
index 9c05c33..62ebe73 100644 (file)
@@ -1085,27 +1085,29 @@ sub get_formname_translation {
   local $::locale = Locale->new($self->{recipient_locale});
 
   my %formname_translations = (
-    bin_list                => $main::locale->text('Bin List'),
-    credit_note             => $main::locale->text('Credit Note'),
-    invoice                 => $main::locale->text('Invoice'),
-    invoice_copy            => $main::locale->text('Invoice Copy'),
-    pick_list               => $main::locale->text('Pick List'),
-    proforma                => $main::locale->text('Proforma Invoice'),
-    purchase_order          => $main::locale->text('Purchase Order'),
-    request_quotation       => $main::locale->text('RFQ'),
-    sales_order             => $main::locale->text('Confirmation'),
-    sales_quotation         => $main::locale->text('Quotation'),
-    storno_invoice          => $main::locale->text('Storno Invoice'),
-    sales_delivery_order    => $main::locale->text('Delivery Order'),
-    purchase_delivery_order => $main::locale->text('Delivery Order'),
-    dunning                 => $main::locale->text('Dunning'),
-    dunning1                => $main::locale->text('Payment Reminder'),
-    dunning2                => $main::locale->text('Dunning'),
-    dunning3                => $main::locale->text('Last Dunning'),
-    dunning_invoice         => $main::locale->text('Dunning Invoice'),
-    letter                  => $main::locale->text('Letter'),
-    ic_supply               => $main::locale->text('Intra-Community supply'),
-    statement               => $main::locale->text('Statement'),
+    bin_list                    => $main::locale->text('Bin List'),
+    credit_note                 => $main::locale->text('Credit Note'),
+    invoice                     => $main::locale->text('Invoice'),
+    invoice_copy                => $main::locale->text('Invoice Copy'),
+    invoice_for_advance_payment => $main::locale->text('Invoice for Advance Payment'),
+    final_invoice               => $main::locale->text('Final Invoice'),
+    pick_list                   => $main::locale->text('Pick List'),
+    proforma                    => $main::locale->text('Proforma Invoice'),
+    purchase_order              => $main::locale->text('Purchase Order'),
+    request_quotation           => $main::locale->text('RFQ'),
+    sales_order                 => $main::locale->text('Confirmation'),
+    sales_quotation             => $main::locale->text('Quotation'),
+    storno_invoice              => $main::locale->text('Storno Invoice'),
+    sales_delivery_order        => $main::locale->text('Delivery Order'),
+    purchase_delivery_order     => $main::locale->text('Delivery Order'),
+    dunning                     => $main::locale->text('Dunning'),
+    dunning1                    => $main::locale->text('Payment Reminder'),
+    dunning2                    => $main::locale->text('Dunning'),
+    dunning3                    => $main::locale->text('Last Dunning'),
+    dunning_invoice             => $main::locale->text('Dunning Invoice'),
+    letter                      => $main::locale->text('Letter'),
+    ic_supply                   => $main::locale->text('Intra-Community supply'),
+    statement                   => $main::locale->text('Statement'),
   );
 
   $main::lxdebug->leave_sub();
@@ -1131,11 +1133,11 @@ sub get_number_prefix_for_type {
   my ($self) = @_;
 
   my $prefix =
-      (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
-    : ($self->{type} =~ /_quotation$/)                        ? 'quo'
-    : ($self->{type} =~ /_delivery_order$/)                   ? 'do'
-    : ($self->{type} =~ /letter/)                             ? 'letter'
-    :                                                           'ord';
+      (first { $self->{type} eq $_ } qw(invoice invoice_for_advance_payment final_invoice credit_note)) ? 'inv'
+    : ($self->{type} =~ /_quotation$/)                                                                  ? 'quo'
+    : ($self->{type} =~ /_delivery_order$/)                                                             ? 'do'
+    : ($self->{type} =~ /letter/)                                                                       ? 'letter'
+    :                                                                                                     'ord';
 
   # better default like this?
   # : ($self->{type} =~ /(sales|purcharse)_order/           :  'ord';
@@ -1170,7 +1172,7 @@ sub generate_attachment_filename {
   my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
   my $prefix              = $self->get_number_prefix_for_type();
 
-  if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
+  if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice invoice_for_advance_payment final_invoice credit_note))) {
     $attachment_filename .= ' (' . $recipient_locale->text('Preview') . ')' . $self->get_extension_for_format();
 
   } elsif ($attachment_filename && $self->{"${prefix}number"}) {
@@ -2944,6 +2946,8 @@ sub save_status {
 # $main::locale->text('UNDO TRANSFER')
 # $main::locale->text('UNIMPORT')
 # $main::locale->text('invoice')
+# $main::locale->text('invoice_for_advance_payment')
+# $main::locale->text('final_invoice')
 # $main::locale->text('proforma')
 # $main::locale->text('sales_order')
 # $main::locale->text('pick_list')
index cf4616f..2b07ee7 100644 (file)
@@ -69,6 +69,12 @@ sub get_print_options {
     ($form->{type} eq 'invoice' && $form->{storno}) ? (
       opthash("storno_invoice",      $form->{PD}{storno_invoice},      $locale->text('Storno Invoice')),
     ) : undef,
+    ($form->{type} eq 'invoice_for_advance_payment') ? (
+      opthash("invoice_for_advance_payment", $form->{PD}{invoice_for_advance_payment},      $locale->text('Invoice for Advance Payment')),
+    ) : undef,
+    ($form->{type} eq 'final_invoice') ? (
+      opthash("final_invoice", $form->{PD}{final_invoice},             $locale->text('Final Invoice')),
+    ) : undef,
     ($form->{type} =~ /_delivery_order$/) ? (
       opthash($form->{type},         $form->{PD}{$form->{type}},       $locale->text('Delivery Order')),
       opthash('pick_list',           $form->{PD}{pick_list},           $locale->text('Pick List')),
index da83c33..2a94607 100644 (file)
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -736,7 +736,7 @@ sub retrieve_accounts {
 
   # transdate madness.
   my $transdate = "";
-  if (($form->{type} eq "invoice") or ($form->{type} eq "credit_note") or ($form->{script} eq 'ir.pl')) {
+  if ( (any {$form->{type} eq $_} qw(invoice credit_note invoice_for_advance_payment final_invoice)) or ($form->{script} eq 'ir.pl') ) {
     # use deliverydate for sales and purchase invoice, if it exists
     # also use deliverydate for credit notes
     $transdate = $form->{tax_point} || $form->{deliverydate} || $form->{invdate};
index 9474b18..11db135 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -35,7 +35,8 @@
 
 package IS;
 
-use List::Util qw(max);
+use List::Util qw(max sum0);
+use List::MoreUtils qw(any);
 
 use Carp;
 use SL::AM;
@@ -51,6 +52,7 @@ use SL::MoreCommon;
 use SL::IC;
 use SL::IO;
 use SL::TransNumber;
+use SL::DB::Chart;
 use SL::DB::Default;
 use SL::DB::Draft;
 use SL::DB::Tax;
@@ -175,7 +177,12 @@ sub invoice_details {
 
   my @payment_arrays = qw(payment paymentaccount paymentdate paymentsource paymentmemo);
 
-  map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @payment_arrays, @prepared_arrays);
+  my @invoices_for_advance_payment_arrays = qw(iap_invnumber iap_transdate
+                                               iap_amount iap_amount_nofmt
+                                               iap_taxamount iap_taxamount_nofmt
+                                               iap_open_amount iap_open_amount_nofmt);
+
+  map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @payment_arrays, @prepared_arrays, @invoices_for_advance_payment_arrays);
 
   my $totalweight = 0;
   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
@@ -560,6 +567,11 @@ sub invoice_details {
     2
   );
 
+  $form->{rounding_nofmt} = $form->{rounding};
+  $form->{total_nofmt}    = $form->{total};
+  $form->{invtotal_nofmt} = $form->{invtotal};
+  $form->{paid_nofmt}     = $form->{paid};
+
   $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
   $form->{total}    = $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
   $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
@@ -574,6 +586,38 @@ sub invoice_details {
   $form->{username} = $myconfig->{name};
   $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
 
+  my $id_for_iap = $form->{convert_from_oe_ids} || $form->{convert_from_ar_ids} || $form->{id};
+  my $from_order = !!$form->{convert_from_oe_ids};
+  foreach my $invoice_for_advance_payment (@{$self->_get_invoices_for_advance_payment($id_for_iap, $from_order)}) {
+    # Collect VAT of invoices for advance payment.
+    # Set sellprices to fxsellprices for items, because
+    # the PriceTaxCalculator sets fxsellprice from sellprice before calculating.
+    $_->sellprice($_->fxsellprice) for @{$invoice_for_advance_payment->items};
+    my %pat       = $invoice_for_advance_payment->calculate_prices_and_taxes;
+    my $taxamount = sum0 values %{ $pat{taxes_by_tax_id} };
+
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_$_"} },                $invoice_for_advance_payment->$_) for qw(invnumber transdate);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount_nofmt"} },      $invoice_for_advance_payment->amount);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount"} },            $invoice_for_advance_payment->amount_as_number);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount_nofmt"} },   $taxamount);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount"} },         $form->format_amount($myconfig, $taxamount, 2));
+
+    my $open_amount = $form->round_amount($invoice_for_advance_payment->open_amount, 2);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_open_amount_nofmt"} }, $open_amount);
+    push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_open_amount"} },       $form->format_amount($myconfig, $open_amount, 2));
+
+    $form->{iap_amount_nofmt}      += $invoice_for_advance_payment->amount;
+    $form->{iap_taxamount_nofmt}   += $taxamount;
+    $form->{iap_open_amount_nofmt} += $open_amount;
+    $form->{iap_existing}           = 1;
+  }
+  $form->{iap_amount}      = $form->format_amount($myconfig, $form->{iap_amount_nofmt},      2);
+  $form->{iap_taxamount}   = $form->format_amount($myconfig, $form->{iap_taxamount_nofmt},   2);
+  $form->{iap_open_amount} = $form->format_amount($myconfig, $form->{iap_open_amount_nofmt}, 2);
+
+  $form->{iap_final_amount_nofmt} = $form->{invtotal_nofmt} - $form->{iap_amount_nofmt};
+  $form->{iap_final_amount}       = $form->format_amount($myconfig, $form->{iap_final_amount_nofmt}, 2);
+
   $main::lxdebug->leave_sub();
 }
 
@@ -714,6 +758,8 @@ sub _post_invoice {
 
   my $all_units = AM->retrieve_units($myconfig, $form);
 
+  my $already_booked = !!$form->{id};
+
   if (!$payments_only) {
     if ($form->{storno}) {
       _delete_transfers($dbh, $form, $form->{storno_id});
@@ -1039,6 +1085,105 @@ SQL
   # entsprechend auch beim Bestimmen des Steuerschlüssels in Taxkey.pm berücksichtigen
   my $taxdate = $form->{tax_point} ||$form->{deliverydate} || $form->{invdate};
 
+  # Sanity checks for invoices for advance payment and final invoices
+  my $advance_payment_clearing_chart;
+  if (any { $_ eq $form->{type} } qw(invoice_for_advance_payment final_invoice)) {
+    $advance_payment_clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load;
+    die "No Clearing Chart for Advance Payment" unless ref $advance_payment_clearing_chart eq 'SL::DB::Chart';
+
+    my @current_taxaccounts = (split(/ /, $form->{taxaccounts}));
+    die 'Wrong call: Cannot post invoice for advance payment or final invoice with more than one tax' if (scalar @current_taxaccounts > 1);
+
+    my @trans_ids = keys %{ $form->{amount} };
+    if (scalar @trans_ids > 1) {
+      require Data::Dumper;
+      die "Invalid state for advance payment more than one trans_id " . Dumper($form->{amount});
+    }
+  }
+
+  my $iap_amounts;
+  if ($form->{type} eq 'final_invoice') {
+    my $id_for_iap = $form->{convert_from_oe_ids} || $form->{convert_from_ar_ids} || $form->{id};
+    my $from_order = !!$form->{convert_from_oe_ids};
+    my $invoices_for_advance_payment = $self->_get_invoices_for_advance_payment($id_for_iap, $from_order);
+    if (scalar @$invoices_for_advance_payment > 0) {
+      # reverse booking for invoices for advance payment
+      foreach my $invoice_for_advance_payment (@$invoices_for_advance_payment) {
+        # delete ?
+        # --> is implemented below (bookings are marked in memo field)
+        #
+        # TODO: helper table acc_trans_advance_payment
+        # trans_id for final invoice connects to acc_trans_id here
+        # my $booking = SL::DB::AccTrans->new( ...)
+        # --> helper table not nessessary because of mark in memo field
+        #
+        # TODO: If final_invoice change (delete storno) delete all connectin acc_trans entries, if
+        # period is not closed
+        # --> no problem because gldate of reverse booking is date of final invoice
+        #     if deletion of final invoice is allowed, reverting bookings in invoices
+        #     for advance payment are allowed, too.
+        # $booking->id, $self->id in helper table
+        if (!$already_booked) {
+          # move all netamount to correct transfer chart (19% or 7%)
+          my %inv_calc = $invoice_for_advance_payment->calculate_prices_and_taxes();
+          my @trans_ids = keys %{ $inv_calc{amounts} };
+          die "Invalid state for advance payment invoice,more than one trans_id" if (scalar @trans_ids > 1);
+          my $entry = delete $inv_calc{amounts}{$trans_ids[0]};
+          my $tax;
+          if ($entry->{tax_id}) {
+            $tax = SL::DB::Manager::Tax->find_by(id => $entry->{tax_id}); # || die "Can't find tax with id " . $entry->{tax_id};
+          }
+          # no tax, no prob
+          if ($tax and $tax->rate != 0) {
+            my $transfer_chart = $tax->taxkey == 2 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_7_id)->load
+                              :  $tax->taxkey == 3 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_19_id)->load
+                              :  undef;
+            die "No Transfer Chart for Advance Payment" unless ref $transfer_chart eq 'SL::DB::Chart';
+            $form->{amount}->{$invoice_for_advance_payment->id}->{$transfer_chart->accno} = -1 * $invoice_for_advance_payment->netamount;
+            $form->{memo}  ->{$invoice_for_advance_payment->id}->{$transfer_chart->accno} = 'reverse booking by final invoice';
+            # AR
+            $form->{amount}->{$invoice_for_advance_payment->id}->{$form->{AR}} = $invoice_for_advance_payment->netamount;
+            $form->{memo}  ->{$invoice_for_advance_payment->id}->{$form->{AR}} = 'reverse booking by final invoice';
+          }
+        }
+
+        # VAT for invoices for advance payment is booked on payment of these. So do not book this VAT for final invoice.
+        # And book the amount of the invoices for advance payment with taxkey 0 (see below).
+        # Collect amounts and VAT of invoices for advance payment.
+
+        # Set sellprices to fxsellprices for items, because
+        # the PriceTaxCalculator sets fxsellprice from sellprice before calculating.
+        $_->sellprice($_->fxsellprice) for @{$invoice_for_advance_payment->items};
+        my %pat = $invoice_for_advance_payment->calculate_prices_and_taxes;
+
+        foreach my $tax_chart_id (keys %{ $pat{taxes_by_chart_id} }) {
+          my $tax_accno = SL::DB::Chart->load_cached($tax_chart_id)->accno;
+          $form->{amount}{ $form->{id} }{$tax_accno}  -= $pat{taxes_by_chart_id}->{$tax_chart_id};
+          $form->{amount}{ $form->{id} }{$form->{AR}} += $pat{taxes_by_chart_id}->{$tax_chart_id};
+        }
+
+        foreach my $amount_chart_id (keys %{ $pat{amounts} }) {
+          my $amount_accno = SL::DB::Chart->load_cached($amount_chart_id)->accno;
+          $iap_amounts->{$amount_accno}                 += $pat{amounts}->{$amount_chart_id}->{amount};
+          $form->{amount}{ $form->{id} }{$amount_accno} -= $pat{amounts}->{$amount_chart_id}->{amount};
+        }
+      }
+    }
+  }
+
+  if ($form->{type} eq 'invoice_for_advance_payment') {
+    # get gross and move to clearing chart - delete everything else
+    # 1. gross
+    my $gross = $form->{amount}{ $form->{id} }{$form->{AR}};
+    # 2. destroy
+    undef $form->{amount}{ $form->{id} };
+    # 3. rebuild
+    $form->{amount}{ $form->{id} }{$form->{AR}}            = $gross;
+    $form->{amount}{ $form->{id} }{$advance_payment_clearing_chart->accno} = $gross * -1;
+    # 4. no cogs, hopefully not commonly used at all
+    undef $form->{amount_cogs};
+  }
+
   foreach my $trans_id (keys %{ $form->{amount_cogs} }) {
     foreach my $accno (keys %{ $form->{amount_cogs}{$trans_id} }) {
       next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
@@ -1076,7 +1221,7 @@ SQL
 
       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
         $query =
-          qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
+          qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link, memo)
              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
                      (SELECT tax_id
                       FROM taxkeys
@@ -1093,8 +1238,9 @@ SQL
                       AND startdate <= ?
                       ORDER BY startdate DESC LIMIT 1),
                      ?,
-                     (SELECT link FROM chart WHERE accno = ?))|;
-        @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
+                     (SELECT link FROM chart WHERE accno = ?),
+                     ?)|;
+        @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno, $form->{memo}{$trans_id}{$accno});
         do_query($form, $dbh, $query, @values);
         $form->{amount}{$trans_id}{$accno} = 0;
       }
@@ -1105,7 +1251,7 @@ SQL
 
       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
         $query =
-          qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
+          qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link, memo)
              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
                      (SELECT tax_id
                       FROM taxkeys
@@ -1122,8 +1268,9 @@ SQL
                       AND startdate <= ?
                       ORDER BY startdate DESC LIMIT 1),
                      ?,
-                     (SELECT link FROM chart WHERE accno = ?))|;
-        @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
+                     (SELECT link FROM chart WHERE accno = ?),
+                     ?)|;
+        @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno,$form->{memo}{$trans_id}{$accno});
         do_query($form, $dbh, $query, @values);
       }
     }
@@ -1137,6 +1284,17 @@ SQL
     }
   }
 
+  # Book the amount of the invoices for advance payment with taxkey 0 (see below).
+  if ($form->{type} eq 'final_invoice' && $iap_amounts) {
+    foreach my $accno (keys %$iap_amounts) {
+      $query =
+        qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
+        VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|;
+      @values = (conv_i($form->{id}), $accno, $iap_amounts->{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno);
+      do_query($form, $dbh, $query, @values);
+    }
+  }
+
   # deduct payment differences from diff
   for my $i (1 .. $form->{paidaccounts}) {
     if ($form->{"paid_$i"} != 0) {
@@ -1459,6 +1617,50 @@ SQL
   return 1;
 }
 
+sub _get_invoices_for_advance_payment {
+  my ($self, $id, $id_is_from_order) = @_;
+
+  return [] if !$id;
+
+  # Search all related invoices for advance payment.
+  # Case 1:
+  # (order) -> invoice for adv. payment 1 -> invoice for adv. payment 2 -> invoice for adv. payment 3 -> final invoice
+  #
+  # Case 2:
+  # order -> invoice for adv. payment 1
+  #   | |`-> invoice for adv. payment 2
+  #   | `--> invoice for adv. payment 3
+  #   `----> final invoice
+  #
+  # The id is currently that from the last invoice for adv. payment (3 in this example),
+  # that from the final invoice or that from the order.
+
+  my $invoice_obj;
+  my $order_obj;
+  my $links;
+
+  if (!$id_is_from_order) {
+    $invoice_obj = SL::DB::Invoice->load_cached($id*1);
+    $links       = $invoice_obj->linked_records(direction => 'from', from => ['Order']);
+    $order_obj   = $links->[0];
+  } else {
+    $order_obj   = SL::DB::Order->load_cached($id*1);
+  }
+
+  if ($order_obj) {
+    $links        = $order_obj  ->linked_records(direction => 'to',   to => ['Invoice']);
+  } else {
+    $links        = $invoice_obj->linked_records(direction => 'from', from => ['Invoice'], recursive => 1);
+  }
+
+  my @related_invoices = grep {'SL::DB::Invoice' eq ref $_ && "invoice_for_advance_payment" eq $_->type} @$links;
+
+  push @related_invoices, $invoice_obj if !$order_obj && "invoice_for_advance_payment" eq $invoice_obj->type;
+
+  return \@related_invoices;
+}
+
+
 sub transfer_out {
   $::lxdebug->enter_sub;
 
@@ -1965,6 +2167,18 @@ sub _delete_invoice {
     do_query($form, $dbh, qq|UPDATE ar SET storno = 'f', paid = 0 WHERE id = ?|, $invoice_id);
   }
 
+  # if we delete a final invoice, the reverse bookings for the clearing account in the invoice for advance payment
+  # must be deleted as well
+  my $invoices_for_advance_payment = $self->_get_invoices_for_advance_payment($form->{id});
+
+  # Todo: allow only if invoice for advance payment is not paid.
+  # die if any { $_->paid } for @$invoices_for_advance_payment;
+  my @trans_ids_to_consider        = map { $_->id } @$invoices_for_advance_payment;
+  if (scalar @trans_ids_to_consider) {
+    my $query = sprintf 'DELETE FROM acc_trans WHERE memo LIKE ? AND trans_id IN (%s)', join ', ', ("?") x scalar @trans_ids_to_consider;
+    do_query($form, $dbh, $query, 'reverse booking by final invoice', @trans_ids_to_consider);
+  }
+
   # delete spool files
   my @spoolfiles = selectall_array_query($form, $dbh, qq|SELECT spoolfile FROM status WHERE trans_id = ?|, @values);
 
index bd3e753..25646da 100644 (file)
@@ -15,7 +15,7 @@ use Rose::Object::MakeMethods::Generic
  scalar => [ qw(type id number save dbh dbh_provided business_id) ],
 );
 
-my @SUPPORTED_TYPES = qw(invoice credit_note customer vendor sales_delivery_order purchase_delivery_order sales_order purchase_order sales_quotation request_quotation part service assembly assortment letter);
+my @SUPPORTED_TYPES = qw(invoice invoice_for_advance_payment final_invoice credit_note customer vendor sales_delivery_order purchase_delivery_order sales_order purchase_order sales_quotation request_quotation part service assembly assortment letter);
 
 sub new {
   my $class = shift;
@@ -37,7 +37,7 @@ sub _get_filters {
   my $type    = $self->type;
   my %filters = ( where => '' );
 
-  if (any { $_ eq $type } qw(invoice credit_note)) {
+  if (any { $_ eq $type } qw(invoice invoice_for_advance_payment final_invoice credit_note)) {
     $filters{trans_number}  = "invnumber";
     $filters{numberfield}   = $type eq 'credit_note' ? "cnnumber" : "invnumber";
     $filters{table}         = "ar";
index 0b27d7d..b0f8909 100644 (file)
@@ -17,24 +17,26 @@ use Rose::Object::MakeMethods::Generic (
 );
 
 my %type_to_path = (
-  sales_quotation         => 'angebote',
-  sales_order             => 'bestellungen',
-  request_quotation       => 'anfragen',
-  purchase_order          => 'lieferantenbestellungen',
-  sales_delivery_order    => 'verkaufslieferscheine',
-  purchase_delivery_order => 'einkaufslieferscheine',
-  credit_note             => 'gutschriften',
-  invoice                 => 'rechnungen',
-  purchase_invoice        => 'einkaufsrechnungen',
-  part                    => 'waren',
-  service                 => 'dienstleistungen',
-  assembly                => 'erzeugnisse',
-  letter                  => 'briefe',
-  general_ledger          => 'dialogbuchungen',
-  accounts_payable        => 'kreditorenbuchungen',
-  customer                => 'kunden',
-  vendor                  => 'lieferanten',
-  dunning                 => 'mahnungen',
+  sales_quotation             => 'angebote',
+  sales_order                 => 'bestellungen',
+  request_quotation           => 'anfragen',
+  purchase_order              => 'lieferantenbestellungen',
+  sales_delivery_order        => 'verkaufslieferscheine',
+  purchase_delivery_order     => 'einkaufslieferscheine',
+  credit_note                 => 'gutschriften',
+  invoice                     => 'rechnungen',
+  invoice_for_advance_payment => 'rechnungen',
+  final_invoice               => 'rechnungen',
+  purchase_invoice            => 'einkaufsrechnungen',
+  part                        => 'waren',
+  service                     => 'dienstleistungen',
+  assembly                    => 'erzeugnisse',
+  letter                      => 'briefe',
+  general_ledger              => 'dialogbuchungen',
+  accounts_payable            => 'kreditorenbuchungen',
+  customer                    => 'kunden',
+  vendor                      => 'lieferanten',
+  dunning                     => 'mahnungen',
 );
 
 sub get_all_files {
index b2a6602..0ed071e 100644 (file)
@@ -1206,12 +1206,23 @@ sub ar_transactions {
     my $is_storno  = $ar->{storno} &&  $ar->{storno_id};
     my $has_storno = $ar->{storno} && !$ar->{storno_id};
 
-    $ar->{type} =
-      $has_storno       ? $locale->text("Invoice with Storno (abbreviation)") :
-      $is_storno        ? $locale->text("Storno (one letter abbreviation)") :
-      $ar->{amount} < 0 ? $locale->text("Credit note (one letter abbreviation)") :
-      $ar->{invoice}    ? $locale->text("Invoice (one letter abbreviation)") :
-                          $locale->text("AR Transaction (abbreviation)");
+    if ($ar->{type} eq 'invoice_for_advance_payment') {
+      $ar->{type} =
+        $has_storno       ? $locale->text("Invoice for Advance Payment with Storno (abbreviation)") :
+        $is_storno        ? $locale->text("Storno (one letter abbreviation)") :
+                            $locale->text("Invoice for Advance Payment (one letter abbreviation)");
+
+    } elsif ($ar->{type} eq 'final_invoice') {
+      $ar->{type} = t8('Final Invoice (one letter abbreviation)');
+
+    } else {
+      $ar->{type} =
+        $has_storno       ? $locale->text("Invoice with Storno (abbreviation)") :
+        $is_storno        ? $locale->text("Storno (one letter abbreviation)") :
+        $ar->{amount} < 0 ? $locale->text("Credit note (one letter abbreviation)") :
+        $ar->{invoice}    ? $locale->text("Invoice (one letter abbreviation)") :
+                            $locale->text("AR Transaction (abbreviation)");
+    }
 
     map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 2) } qw(netamount tax amount paid due marge_total marge_percent);
 
index 9628ddf..2f0fb95 100644 (file)
@@ -1205,6 +1205,15 @@ sub print_form {
   if ($form->{formname} eq "invoice") {
     $form->{label} = $locale->text('Invoice');
   }
+
+  if ($form->{formname} eq "invoice_for_advance_payment") {
+    $form->{label} = $locale->text('Invoice for Advance Payment');
+  }
+
+  if ($form->{formname} eq "final_invoice") {
+    $form->{label} = $locale->text('Final Invoice');
+  }
+
   if ($form->{formname} eq 'sales_order') {
     $inv                  = "ord";
     $due                  = "req";
@@ -1302,7 +1311,7 @@ sub print_form {
   }
 
   $form->{TEMPLATE_DRIVER_OPTIONS} = { };
-  if (any { $form->{type} eq $_ } qw(sales_quotation sales_order sales_delivery_order invoice request_quotation purchase_order purchase_delivery_order credit_note)) {
+  if (any { $form->{type} eq $_ } qw(sales_quotation sales_order sales_delivery_order invoice invoice_for_advance_payment final_invoice request_quotation purchase_order purchase_delivery_order credit_note)) {
     $form->{TEMPLATE_DRIVER_OPTIONS}->{variable_content_types} = $form->get_variable_content_types();
   }
 
@@ -1885,6 +1894,8 @@ sub _make_record_item {
     sales_quotation         => 'OrderItem',
     request_quotation       => 'OrderItem',
     invoice                 => 'InvoiceItem',
+    invoice_for_advance_payment => 'InvoiceItem',
+    final_invoice           => 'InvoiceItem',
     credit_note             => 'InvoiceItem',
     purchase_invoice        => 'InvoiceItem',
     purchase_delivery_order => 'DeliveryOrderItem',
index 12deb26..f024622 100644 (file)
@@ -36,9 +36,11 @@ use SL::FU;
 use SL::IS;
 use SL::OE;
 use SL::MoreCommon qw(restore_form save_form);
+use SL::RecordLinks;
+
 use Data::Dumper;
 use DateTime;
-use List::MoreUtils qw(uniq);
+use List::MoreUtils qw(any uniq);
 use List::Util qw(max sum);
 use List::UtilsBy qw(sort_by);
 use English qw(-no_match_vars);
@@ -89,6 +91,13 @@ sub add {
     if ($form->{storno}) {
       $form->{title} = $locale->text('Add Storno Credit Note');
     }
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{title} = $locale->text('Add Invoice for Advance Payment');
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{title} = $locale->text('Add Final Invoice');
+
   } else {
     $form->{title} = $locale->text('Add Sales Invoice');
 
@@ -132,6 +141,14 @@ sub edit {
   if ($form->{type} eq "credit_note") {
     $form->{title} = $locale->text('Edit Credit Note');
     $form->{title} = $locale->text('Edit Storno Credit Note') if $form->{storno};
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{title} = $locale->text('Edit Invoice for Advance Payment');
+    $form->{title} = $locale->text('Edit Storno Invoice for Advance Payment') if $form->{storno};
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{title} = $locale->text('Edit Final Invoice');
+
   } else {
     $form->{title} = $locale->text('Edit Sales Invoice');
     $form->{title} = $locale->text('Edit Storno Invoice')     if $form->{storno};
@@ -237,8 +254,18 @@ sub prepare_invoice {
   if ($form->{type} eq "credit_note") {
     $form->{type}     = "credit_note";
     $form->{formname} = "credit_note";
+
+  } elsif ($form->{type} eq "invoice_for_advance_payment") {
+    $form->{type}     = "invoice_for_advance_payment";
+    $form->{formname} = "invoice_for_advance_payment";
+
+  } elsif ($form->{type} eq "final_invoice") {
+    $form->{type}     = "final_invoice";
+    $form->{formname} = "final_invoice";
+
   } elsif ($form->{formname} eq "proforma" ) {
     $form->{type}     = "invoice";
+
   } else {
     $form->{type}     = "invoice";
     $form->{formname} = "invoice";
@@ -294,6 +321,28 @@ sub setup_is_action_bar {
   if ($::instance_conf->get_warn_no_delivery_order_for_invoice && !$form->{id}) {
     $warn_unlinked_delivery_order = 1 unless $form->{convert_from_do_ids};
   }
+
+  my $has_further_invoice_for_advance_payment;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'to', to => ['Invoice']);
+    $has_further_invoice_for_advance_payment = any {'SL::DB::Invoice' eq ref $_ && "invoice_for_advance_payment" eq $_->type} @$lr;
+  }
+
+  my $has_final_invoice;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'to', to => ['Invoice']);
+    $has_final_invoice = any {'SL::DB::Invoice' eq ref $_ && "final_invoice" eq $_->invoice_type} @$lr;
+  }
+
+  my $is_invoice_for_advance_payment_from_order;
+  if ($form->{id} && $form->{type} eq "invoice_for_advance_payment") {
+    my $invoice_obj = SL::DB::Invoice->load_cached($form->{id});
+    my $lr          = $invoice_obj->linked_records(direction => 'from', from => ['Order']);
+    $is_invoice_for_advance_payment_from_order = scalar @$lr >= 1;
+  }
+
   for my $bar ($::request->layout->get('actionbar')) {
     $bar->add(
       action => [
@@ -328,6 +377,7 @@ sub setup_is_action_bar {
                     : !$form->{id}                ? t8('This invoice has not been posted yet.')
                     : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.')
                     :                               undef,
+          only_if  => $form->{type} ne "invoice_for_advance_payment",
         ],
         action => [ t8('Mark as paid'),
           submit   => [ '#form', { action => "mark_as_paid" } ],
@@ -335,7 +385,7 @@ sub setup_is_action_bar {
           disabled => !$may_edit_create ? t8('You must not change this invoice.')
                     : !$form->{id}      ? t8('This invoice has not been posted yet.')
                     :                     undef,
-          only_if  => $::instance_conf->get_is_show_mark_as_paid,
+          only_if  => $::instance_conf->get_is_show_mark_as_paid && $form->{type} ne "invoice_for_advance_payment",
         ],
       ], # end of combobox "Post"
 
@@ -377,6 +427,30 @@ sub setup_is_action_bar {
                     : !$form->{id}      ? t8('This invoice has not been posted yet.')
                     :                     undef,
         ],
+        action => [
+          t8('Further Invoice for Advance Payment'),
+          submit   => [ '#form', { action => "further_invoice_for_advance_payment" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create                          ? t8('You must not change this invoice.')
+                    : !$form->{id}                               ? t8('This invoice has not been posted yet.')
+                    : $has_further_invoice_for_advance_payment   ? t8('This invoice has already a further invoice for advanced payment.')
+                    : $has_final_invoice                         ? t8('This invoice has already a final invoice.')
+                    : $is_invoice_for_advance_payment_from_order ? t8('This invoice was added from an order. See there.')
+                    :                                              undef,
+          only_if  => $form->{type} eq "invoice_for_advance_payment",
+        ],
+        action => [
+          t8('Final Invoice'),
+          submit   => [ '#form', { action => "final_invoice" } ],
+          checks   => [ 'kivi.validate_form' ],
+          disabled => !$may_edit_create                          ? t8('You must not change this invoice.')
+                    : !$form->{id}                               ? t8('This invoice has not been posted yet.')
+                    : $has_further_invoice_for_advance_payment   ? t8('This invoice has a further invoice for advanced payment.')
+                    : $has_final_invoice                         ? t8('This invoice has already a final invoice.')
+                    : $is_invoice_for_advance_payment_from_order ? t8('This invoice was added from an order. See there.')
+                    :                                              undef,
+          only_if  => $form->{type} eq "invoice_for_advance_payment",
+        ],
         action => [
           t8('Credit Note'),
           submit   => [ '#form', { action => "credit_note" } ],
@@ -550,10 +624,11 @@ sub form_header {
     $form->{"select$item"} =~ s/option>\Q$form->{$item}\E/option selected>$form->{$item}/;
   }
 
-  $TMPL_VAR{is_type_credit_note} = $form->{type}   eq "credit_note";
-  $TMPL_VAR{is_format_html}      = $form->{format} eq 'html';
-  $TMPL_VAR{dateformat}          = $myconfig{dateformat};
-  $TMPL_VAR{numberformat}        = $myconfig{numberformat};
+  $TMPL_VAR{is_type_normal_invoice} = $form->{type} eq "invoice";
+  $TMPL_VAR{is_type_credit_note}    = $form->{type}   eq "credit_note";
+  $TMPL_VAR{is_format_html}         = $form->{format} eq 'html';
+  $TMPL_VAR{dateformat}             = $myconfig{dateformat};
+  $TMPL_VAR{numberformat}           = $myconfig{numberformat};
 
   # hiddens
   $TMPL_VAR{HIDDENS} = [qw(
@@ -693,17 +768,18 @@ sub form_footer {
   }
 
   print $form->parse_html_template('is/form_footer', {
-    is_type_credit_note => ($form->{type} eq "credit_note"),
-    totalpaid           => $totalpaid,
-    paid_missing        => $form->{invtotal} - $totalpaid,
-    print_options       => setup_sales_purchase_print_options(),
-    show_storno         => $form->{id} && !$form->{storno} && !IS->has_storno(\%myconfig, $form, "ar") && !$totalpaid,
-    show_delete         => ($::instance_conf->get_is_changeable == 2)
-                             ? ($form->current_date(\%myconfig) eq $form->{gldate})
-                             : ($::instance_conf->get_is_changeable == 1),
-    today               => DateTime->today,
-    vc_obj              => $form->{customer_id} ? SL::DB::Customer->load_cached($form->{customer_id}) : undef,
-    shipto_cvars        => $shipto_cvars,
+    is_type_normal_invoice              => ($form->{type} eq "invoice"),
+    is_type_credit_note                 => ($form->{type} eq "credit_note"),
+    totalpaid                           => $totalpaid,
+    paid_missing                        => $form->{invtotal} - $totalpaid,
+    print_options                       => setup_sales_purchase_print_options(),
+    show_storno                         => $form->{id} && !$form->{storno} && !IS->has_storno(\%myconfig, $form, "ar") && !$totalpaid,
+    show_delete                         => ($::instance_conf->get_is_changeable == 2)
+                                             ? ($form->current_date(\%myconfig) eq $form->{gldate})
+                                             : ($::instance_conf->get_is_changeable == 1),
+    today                               => DateTime->today,
+    vc_obj                              => $form->{customer_id} ? SL::DB::Customer->load_cached($form->{customer_id}) : undef,
+    shipto_cvars                        => $shipto_cvars,
   });
 ##print $form->parse_html_template('is/_payments'); # parser
 ##print $form->parse_html_template('webdav/_list'); # parser
@@ -967,6 +1043,12 @@ sub post {
 
   $form->isblank("exchangerate", $locale->text('Exchangerate missing!'))
     if ($form->{currency} ne $form->{defaultcurrency});
+  # advance payment allows only one tax
+  if ($form->{type} eq 'invoice_for_advance_payment') {
+    my @current_taxaccounts = (split(/ /, $form->{taxaccounts}));
+    $form->error($locale->text('Cannot post invoice for advance payment with more than one tax'))
+      if (scalar @current_taxaccounts > 1);
+  }
 
   for my $i (1 .. $form->{paidaccounts}) {
     if ($form->parse_amount(\%myconfig, $form->{"paid_$i"})) {
@@ -1111,6 +1193,80 @@ sub use_as_new {
   $main::lxdebug->leave_sub();
 }
 
+sub further_invoice_for_advance_payment {
+  my $form     = $main::form;
+  my %myconfig = %main::myconfig;
+
+  $main::auth->assert('invoice_edit');
+
+  delete @{ $form }{qw(printed emailed queued invnumber invdate exchangerate forex deliverydate datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno locked)};
+  $form->{convert_from_ar_ids} = $form->{id};
+  $form->{id}                  = '';
+  $form->{rowcount}--;
+  $form->{paidaccounts}        = 1;
+  $form->{invdate}             = $form->current_date(\%myconfig);
+  my $terms                    = get_payment_terms_for_invoice();
+  $form->{duedate}             = $terms ? $terms->calc_date(reference_date => $form->{invdate})->to_kivitendo : $form->{invdate};
+  $form->{employee_id}         = SL::DB::Manager::Employee->current->id;
+  $form->{forex}               = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
+  $form->{exchangerate}        = $form->{forex} if $form->{forex};
+
+  $form->{"converted_from_invoice_id_$_"} = delete $form->{"invoice_id_$_"} for 1 .. $form->{"rowcount"};
+
+  &display_form;
+}
+
+sub final_invoice {
+  my $form     = $main::form;
+  my %myconfig = %main::myconfig;
+
+  $main::auth->assert('invoice_edit');
+
+  my $related_invoices = IS->_get_invoices_for_advance_payment($form->{id});
+
+  delete @{ $form }{qw(printed emailed queued invnumber invdate exchangerate forex deliverydate datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno locked)};
+
+  $form->{convert_from_ar_ids} = $form->{id};
+  $form->{id}                  = '';
+  $form->{type}                = 'final_invoice';
+  $form->{title}               = t8('Edit Final Invoice');
+  $form->{paidaccounts}        = 1;
+  $form->{invdate}             = $form->current_date(\%myconfig);
+  my $terms                    = get_payment_terms_for_invoice();
+  $form->{duedate}             = $terms ? $terms->calc_date(reference_date => $form->{invdate})->to_kivitendo : $form->{invdate};
+  $form->{employee_id}         = SL::DB::Manager::Employee->current->id;
+  $form->{forex}               = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
+  $form->{exchangerate}        = $form->{forex} if $form->{forex};
+
+  foreach my $i (1 .. $form->{"rowcount"}) {
+    delete $form->{"id_$i"};
+    delete $form->{"invoice_id_$i"};
+    delete $form->{"parts_id_$i"};
+    delete $form->{"partnumber_$i"};
+    delete $form->{"description_$i"};
+  }
+
+  remove_emptied_rows(1);
+
+  my $i = 0;
+  foreach my $ri (@$related_invoices) {
+    foreach my $item (@{$ri->items_sorted}) {
+      $i++;
+      $form->{"id_$i"}         = $item->parts_id;
+      $form->{"partnumber_$i"} = $item->part->partnumber;
+      $form->{"discount_$i"}   = $item->discount*100.0;
+      $form->{"sellprice_$i"}  = $item->fxsellprice;
+      $form->{$_ . "_" . $i}   = $item->$_       for qw(description longdescription qty price_factor_id unit active_price_source active_discount_source);
+
+      $form->{$_ . "_" . $i}   = $form->format_amount(\%myconfig, $form->{$_ . "_" . $i}) for qw(qty sellprice discount);
+    }
+  }
+  $form->{rowcount} = $i;
+
+  update();
+  $::dispatcher->end_request;
+}
+
 sub storno {
   $main::lxdebug->enter_sub();
 
@@ -1240,7 +1396,6 @@ sub credit_note {
 
   &prepare_invoice;
 
-
   &display_form;
 
   $main::lxdebug->leave_sub();
index a3425ec..96764eb 100644 (file)
@@ -1654,7 +1654,7 @@ sub invoice {
     $::dispatcher->end_request;
   }
 
-  _oe_remove_delivered_or_billed_rows(id => $form->{id}, type => 'billed');
+  _oe_remove_delivered_or_billed_rows(id => $form->{id}, type => 'billed') if $form->{new_invoice_type} ne 'final_invoice';
 
   $form->{cp_id} *= 1;
 
@@ -1699,7 +1699,9 @@ sub invoice {
 
   if (   $form->{type} eq 'sales_order'
       || $form->{type} eq 'sales_quotation') {
-    $form->{title}  = $locale->text('Add Sales Invoice');
+    $form->{title}  = ($form->{new_invoice_type} eq 'invoice_for_advance_payment') ? $locale->text('Add Invoice for Advance Payment')
+                    : ($form->{new_invoice_type} eq 'final_invoice')               ? $locale->text('Add Final Invoice')
+                    : $locale->text('Add Sales Invoice');
     $form->{script} = 'is.pl';
     $script         = "is";
     $buysell        = 'buy';
@@ -1708,7 +1710,7 @@ sub invoice {
   # bo creates the id, reset it
   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
   $form->{ $form->{vc} } =~ s/--.*//g;
-  $form->{type} = "invoice";
+  $form->{type} = $form->{new_invoice_type} || "invoice";
 
   # locale messages
   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
index 29feb69..3bd1203 100755 (executable)
@@ -176,9 +176,11 @@ $self->{texts} = {
   'Add Delivery Order'          => 'Lieferschein erfassen',
   'Add Document from \'#1\''    => 'Dokument von \'#1\' hinzufügen',
   'Add Dunning'                 => 'Mahnung erzeugen',
+  'Add Final Invoice'           => 'Schlussrechnung erfassen',
   'Add Follow-Up'               => 'Wiedervorlage erstellen',
   'Add Follow-Up for #1'        => 'Wiedervorlage für #1 erstellen',
   'Add General Ledger Transaction' => 'Dialogbuchen',
+  'Add Invoice for Advance Payment' => 'Anzahlungsrechnung erfassen',
   'Add Letter'                  => 'Brief hinzufügen',
   'Add Part'                    => 'Ware erfassen',
   'Add Price Factor'            => 'Preisfaktor erfassen',
@@ -586,6 +588,7 @@ $self->{texts} = {
   'Cannot post a transaction without a value!' => 'Eine Buchung ohne Betrag kann nicht vorgenommen werden!',
   'Cannot post invoice and/or transfer out! Error message:' => 'Rechnung kann nicht gebucht oder es kann nicht ausgelagert werden. Fehlermeldung:',
   'Cannot post invoice for a closed period!' => 'Das Rechnungsdatum fällt in einen abgeschlossen Zeitraum!',
+  'Cannot post invoice for advance payment with more than one tax' => 'Anzahlungsrechnung mit mehr als einem Steuersatz kann nicht gebucht werden',
   'Cannot post invoice!'        => 'Rechnung kann nicht gebucht werden!',
   'Cannot post payment for a closed period!' => 'Es können keine Zahlungen für abgeschlossene Bücher gebucht werden!',
   'Cannot post payment!'        => 'Zahlung kann nicht gebucht werden!',
@@ -668,6 +671,7 @@ $self->{texts} = {
   'Cleared Balance'             => 'abgeschlossen',
   'Cleared/uncleared only'      => 'Status abgeglichen',
   'Clearing Tax Received (No 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
+  'Clearing account for advance payments' => 'Verrechnungskonto für Anzahlungen',
   'Client'                      => 'Mandant',
   'Client #1'                   => 'Mandant #1',
   'Client Configuration'        => 'Mandantenkonfiguration',
@@ -1244,9 +1248,11 @@ $self->{texts} = {
   'Edit Dunning Process Config' => 'Mahnwesenkonfiguration bearbeiten',
   'Edit Employee #1'            => 'Benutzer #1 bearbeiten',
   'Edit Factur-X/ZUGFeRD notes' => 'Factur-X-/ZUGFeRD-Notizen bearbeiten',
+  'Edit Final Invoice'          => 'Schlussrechnung bearbeiten',
   'Edit Follow-Up'              => 'Wiedervorlage bearbeiten',
   'Edit Follow-Up for #1'       => 'Wiedervorlage für #1 bearbeiten',
   'Edit General Ledger Transaction' => 'Buchung im Hauptbuch bearbeiten',
+  'Edit Invoice for Advance Payment' => 'Anzahlungsrechnung bearbeiten',
   'Edit Letter'                 => 'Brief bearbeiten',
   'Edit Part'                   => 'Ware bearbeiten',
   'Edit Preferences for #1'     => 'Einstellungen von #1 bearbeiten',
@@ -1263,6 +1269,7 @@ $self->{texts} = {
   'Edit Service'                => 'Dienstleistung bearbeiten',
   'Edit Storno Credit Note'     => 'Storno Gutschrift bearbeiten',
   'Edit Storno Invoice'         => 'Stornorechnung bearbeiten',
+  'Edit Storno Invoice for Advance Payment' => 'Storno-Anzahlungsrechnung bearbeiten',
   'Edit User'                   => 'Benutzerdaten bearbeiten',
   'Edit User Group'             => 'Benutzergruppe bearbeiten',
   'Edit Vendor'                 => 'Lieferant editieren',
@@ -1551,6 +1558,8 @@ $self->{texts} = {
   'Filter for item variables'   => 'Filter für benutzerdefinierte Artikelvariablen',
   'Filter parts'                => 'Artikel filtern',
   'Filter record template'      => 'Filter für Buchungsvorlagen',
+  'Final Invoice'               => 'Schlussrechnung',
+  'Final Invoice (one letter abbreviation)' => 'F',
   'Financial Controlling'       => 'Finanzcontrolling',
   'Financial Controlling Report' => 'Finanzcontrollingbericht',
   'Financial Overview'          => 'Finanzübersicht',
@@ -1618,6 +1627,7 @@ $self->{texts} = {
   'Function block actions'      => 'Funktionsblockaktionen',
   'Function block number format' => 'Format der Funktionsblocknummerierung',
   'Function/position'           => 'Funktion/Position',
+  'Further Invoice for Advance Payment' => 'Weitere Anzahlungsrechnung',
   'GL Transaction'              => 'Dialogbuchung',
   'GL Transaction (abbreviation)' => 'DB',
   'GL Transactions'             => 'Dialogbuchungen',
@@ -1872,6 +1882,9 @@ $self->{texts} = {
   'Invoice email and Contact Person' => 'E-Mail des Rechnungsempfängers und CC an Ansprechpartner',
   'Invoice email settings'      => 'E-Mail Rechnungsversand',
   'Invoice filter'              => 'Rechnungsfilter',
+  'Invoice for Advance Payment' => 'Anzahlungsrechnung',
+  'Invoice for Advance Payment (one letter abbreviation)' => 'A',
+  'Invoice for Advance Payment with Storno (abbreviation)' => 'A(S)',
   'Invoice for fees'            => 'Rechnung über Gebühren',
   'Invoice has already been storno\'d!' => 'Diese Rechnung wurde bereits storniert.',
   'Invoice number'              => 'Rechnungsnummer',
@@ -2991,7 +3004,10 @@ $self->{texts} = {
   'Save and Close'              => 'Speichern und schließen',
   'Save and Delivery Order'     => 'Speichern und Lieferschein',
   'Save and E-mail'             => 'Speichern und E-Mail',
+  'Save and Final Invoice'      => 'Speichern und Schlussrechnung',
+  'Save and Further Invoice for Advance Payment' => 'Speichern und weitere Anzahlungsrechnung',
   'Save and Invoice'            => 'Speichern und Rechnung erfassen',
+  'Save and Invoice for Advance Payment' => 'Speichern und Anzahlungsrechnung',
   'Save and Order'              => 'Speichern und Auftrag erfassen',
   'Save and Purchase Order'     => 'Speichern und Lieferantenauftrag',
   'Save and Quotation'          => 'Speichern und Angebot',
@@ -3842,10 +3858,14 @@ $self->{texts} = {
   'This group is valid for the following clients' => 'Diese Gruppe ist für die folgenden Mandanten gültig',
   'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' => 'Das wurde in dieser Version umgestellt, bitte ändern Sie die Freitext-Lagerplätze auf vorhandene Lagerplätze.',
   'This has been changed in this version.' => 'Ab dieser Version ist dies nicht mehr so.',
+  'This invoice has a further invoice for advanced payment.' => 'Diese Rechnung hat eine weitere Anzahlungsrechnung.',
+  'This invoice has already a final invoice.' => 'Diese Rechnung hat schon eine Schlussrechnung.',
+  'This invoice has already a further invoice for advanced payment.' => 'Diese Rechnung hat schon eine weitere Anzahlungsrechnung.',
   'This invoice has already been posted.' => 'Die Rechnung wurde bereits gebucht.',
   'This invoice has been canceled already.' => 'Die Rechnung wurde bereits storniert.',
   'This invoice has been linked with a sepa export, undo this first.' => 'Diese Rechnung ist mit einem SEPA-Export verknüpft. Bitte diese Verknüpfung zuerst aufheben.',
   'This invoice has not been posted yet.' => 'Die Rechnung wurde noch nicht gebucht.',
+  'This invoice was added from an order. See there.' => 'Diese Rechnung wurde aus einem Auftrag erstellt. Siehe dort.',
   'This invoice\'s dunning level: #1' => 'Mahnstufe dieser Rechnung: #1',
   'This is a very critical problem.' => 'Dieses Problem ist sehr schwerwiegend.',
   'This is the client to be selected by default on the login screen.' => 'Dies ist derjenige Mandant, der im Loginbildschirm standardmäßig ausgewählt sein wird.',
@@ -3862,6 +3882,7 @@ $self->{texts} = {
   'This option controls the method used for determining the startdate for the balance report.' => 'Diese Option bestimmt, wie das Startdatum für den Bilanzbericht ermittelt wird',
   'This option controls the method used for profit determination.' => 'Dieser Parameter legt die Berechnungsmethode für die Gewinnermittlung fest.',
   'This option controls the posting and calculation behavior for the accounting method.' => 'Dieser Parameter steuert die Buchungs- und Berechnungsmethoden für die Versteuerungsart.',
+  'This order has already a final invoice.' => 'Dieser Auftrag hat schon eine Schlussrechnung.',
   'This part has already been added.' => 'Dieser Artikel wurde schon hinzugefügt',
   'This part was already counted for this bin:' => 'Dieser Artikel wurde für diesen Lagerplatz bereits erfasst:',
   'This price has since gone down' => 'Dieser Preis ist mittlerweile niedriger',
@@ -4408,6 +4429,7 @@ $self->{texts} = {
   'filename'                    => 'Dateiname',
   'filename has not uploadable characters ' => 'Bitte Dateinamen ändern. Er hat für den Upload nicht verwendbare Sonderzeichen ',
   'filesize too big: '          => 'Datei zu groß: ',
+  'final_invoice'               => 'Schlussrechnung',
   'flat-rate position'          => 'Pauschalposition',
   'follow_up_list'              => 'wiedervorlageliste',
   'for'                         => 'für',
@@ -4434,6 +4456,7 @@ $self->{texts} = {
   'internal error (see details)' => 'Interner Fehler (siehe Details)!',
   'invoice'                     => 'Rechnung',
   'invoice mode or item mode'   => 'Rechnungsmodus oder Artikelmodus',
+  'invoice_for_advance_payment' => 'Anzahlungsrechnung',
   'invoice_list'                => 'debitorenbuchungsliste',
   'is'                          => 'ist',
   'is after'                    => 'ist nach dem',
index 79e8f6b..9c747ff 100644 (file)
@@ -176,9 +176,11 @@ $self->{texts} = {
   'Add Delivery Order'          => '',
   'Add Document from \'#1\''    => '',
   'Add Dunning'                 => '',
+  'Add Final Invoice'           => '',
   'Add Follow-Up'               => '',
   'Add Follow-Up for #1'        => '',
   'Add General Ledger Transaction' => '',
+  'Add Invoice for Advance Payment' => '',
   'Add Letter'                  => '',
   'Add Part'                    => '',
   'Add Price Factor'            => '',
@@ -586,6 +588,7 @@ $self->{texts} = {
   'Cannot post a transaction without a value!' => '',
   'Cannot post invoice and/or transfer out! Error message:' => '',
   'Cannot post invoice for a closed period!' => '',
+  'Cannot post invoice for advance payment with more than one tax' => '',
   'Cannot post invoice!'        => '',
   'Cannot post payment for a closed period!' => '',
   'Cannot post payment!'        => '',
@@ -668,6 +671,7 @@ $self->{texts} = {
   'Cleared Balance'             => '',
   'Cleared/uncleared only'      => '',
   'Clearing Tax Received (No 71)' => '',
+  'Clearing account for advance payments' => '',
   'Client'                      => '',
   'Client #1'                   => '',
   'Client Configuration'        => '',
@@ -1244,9 +1248,11 @@ $self->{texts} = {
   'Edit Dunning Process Config' => '',
   'Edit Employee #1'            => '',
   'Edit Factur-X/ZUGFeRD notes' => '',
+  'Edit Final Invoice'          => '',
   'Edit Follow-Up'              => '',
   'Edit Follow-Up for #1'       => '',
   'Edit General Ledger Transaction' => '',
+  'Edit Invoice for Advance Payment' => '',
   'Edit Letter'                 => '',
   'Edit Part'                   => '',
   'Edit Preferences for #1'     => '',
@@ -1263,6 +1269,7 @@ $self->{texts} = {
   'Edit Service'                => '',
   'Edit Storno Credit Note'     => '',
   'Edit Storno Invoice'         => '',
+  'Edit Storno Invoice for Advance Payment' => '',
   'Edit User'                   => '',
   'Edit User Group'             => '',
   'Edit Vendor'                 => '',
@@ -1551,6 +1558,8 @@ $self->{texts} = {
   'Filter for item variables'   => '',
   'Filter parts'                => '',
   'Filter record template'      => '',
+  'Final Invoice'               => '',
+  'Final Invoice (one letter abbreviation)' => '',
   'Financial Controlling'       => '',
   'Financial Controlling Report' => '',
   'Financial Overview'          => '',
@@ -1618,6 +1627,7 @@ $self->{texts} = {
   'Function block actions'      => '',
   'Function block number format' => '',
   'Function/position'           => '',
+  'Further Invoice for Advance Payment' => '',
   'GL Transaction'              => '',
   'GL Transaction (abbreviation)' => '',
   'GL Transactions'             => '',
@@ -1872,6 +1882,9 @@ $self->{texts} = {
   'Invoice email and Contact Person' => '',
   'Invoice email settings'      => '',
   'Invoice filter'              => '',
+  'Invoice for Advance Payment' => '',
+  'Invoice for Advance Payment (one letter abbreviation)' => '',
+  'Invoice for Advance Payment with Storno (abbreviation)' => '',
   'Invoice for fees'            => '',
   'Invoice has already been storno\'d!' => '',
   'Invoice number'              => '',
@@ -2989,7 +3002,10 @@ $self->{texts} = {
   'Save and Close'              => '',
   'Save and Delivery Order'     => '',
   'Save and E-mail'             => '',
+  'Save and Final Invoice'      => '',
+  'Save and Further Invoice for Advance Payment' => '',
   'Save and Invoice'            => '',
+  'Save and Invoice for Advance Payment' => '',
   'Save and Order'              => '',
   'Save and Purchase Order'     => '',
   'Save and Quotation'          => '',
@@ -3837,10 +3853,14 @@ $self->{texts} = {
   'This group is valid for the following clients' => '',
   'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' => '',
   'This has been changed in this version.' => '',
+  'This invoice has a further invoice for advanced payment.' => '',
+  'This invoice has already a final invoice.' => '',
+  'This invoice has already a further invoice for advanced payment.' => '',
   'This invoice has already been posted.' => '',
   'This invoice has been canceled already.' => '',
   'This invoice has been linked with a sepa export, undo this first.' => '',
   'This invoice has not been posted yet.' => '',
+  'This invoice was added from an order. See there.' => '',
   'This invoice\'s dunning level: #1' => '',
   'This is a very critical problem.' => '',
   'This is the client to be selected by default on the login screen.' => '',
@@ -3857,6 +3877,7 @@ $self->{texts} = {
   'This option controls the method used for determining the startdate for the balance report.' => '',
   'This option controls the method used for profit determination.' => '',
   'This option controls the posting and calculation behavior for the accounting method.' => '',
+  'This order has already a final invoice.' => '',
   'This part has already been added.' => '',
   'This part was already counted for this bin:' => '',
   'This price has since gone down' => '',
@@ -4404,6 +4425,7 @@ $self->{texts} = {
   'filename'                    => '',
   'filename has not uploadable characters ' => '',
   'filesize too big: '          => '',
+  'final_invoice'               => '',
   'flat-rate position'          => '',
   'follow_up_list'              => '',
   'for'                         => '',
@@ -4430,6 +4452,7 @@ $self->{texts} = {
   'internal error (see details)' => '',
   'invoice'                     => '',
   'invoice mode or item mode'   => '',
+  'invoice_for_advance_payment' => '',
   'invoice_list'                => '',
   'is'                          => '',
   'is after'                    => '',
diff --git a/menus/user/20-invoice-for-advance-payment.yaml b/menus/user/20-invoice-for-advance-payment.yaml
new file mode 100644 (file)
index 0000000..b76dcad
--- /dev/null
@@ -0,0 +1,10 @@
+- parent: ar
+  id: ar_add_sales_invoice_for_advance_payment
+  name: Add Invoice for Advance Payment
+  icon: sales_invoice_add
+  order: 550
+  access: invoice_edit
+  module: is.pl
+  params:
+    action: add
+    type: invoice_for_advance_payment
diff --git a/sql/Pg-upgrade2/defaults_advance_payment_clearing_chart_id.sql b/sql/Pg-upgrade2/defaults_advance_payment_clearing_chart_id.sql
new file mode 100644 (file)
index 0000000..53da908
--- /dev/null
@@ -0,0 +1,32 @@
+-- @tag: defaults_advance_payment_clearing_chart_id
+-- @description: Voreingestelltes Konto für Verrechnung von Anzahlungen
+-- @depends: new_chart_1593_1495
+
+ALTER TABLE defaults ADD COLUMN advance_payment_clearing_chart_id INTEGER;
+
+DO $$
+BEGIN
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      clearing_accno text := '1593';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_clearing_chart_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      clearing_accno text := '1495';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_clearing_chart_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+END $$;
diff --git a/sql/Pg-upgrade2/defaults_advance_payment_transfer_charts.sql b/sql/Pg-upgrade2/defaults_advance_payment_transfer_charts.sql
new file mode 100644 (file)
index 0000000..f77b257
--- /dev/null
@@ -0,0 +1,59 @@
+-- @tag: defaults_advance_payment_transfer_charts
+-- @description: Standardkonten für erhaltene versteuerte Anzahlungen 7% und 19% setzen
+-- @depends:new_chart_3260_1711 new_chart_3272_1718 defaults_advance_payment_clearing_chart_id
+
+
+ALTER TABLE defaults ADD COLUMN advance_payment_taxable_19_id INTEGER;
+ALTER TABLE defaults ADD COLUMN advance_payment_taxable_7_id  INTEGER;
+UPDATE chart set link ='AR' where id = (select advance_payment_clearing_chart_id from defaults);
+
+
+DO $$
+BEGIN
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      clearing_accno text := '1718';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_taxable_19_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      clearing_accno text := '3272';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_taxable_19_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      clearing_accno text := '1711';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_taxable_7_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      clearing_accno text := '3260';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE clearing_accno ) = 1 THEN
+        UPDATE defaults SET advance_payment_taxable_7_id = (SELECT id FROM chart WHERE accno LIKE clearing_accno);
+      END IF;
+    END;
+  END IF;
+
+
+END $$;
diff --git a/sql/Pg-upgrade2/file_storage_partial_invoices.sql b/sql/Pg-upgrade2/file_storage_partial_invoices.sql
new file mode 100644 (file)
index 0000000..7fed883
--- /dev/null
@@ -0,0 +1,18 @@
+-- @tag: file_storage_partial_invoices
+-- @description: Dateispeicher auch für Anzahlungs- und Schlussrechnung
+-- @depends: file_storage_project
+
+ALTER TABLE files
+  DROP CONSTRAINT valid_type;
+ALTER TABLE files
+  ADD  CONSTRAINT valid_type CHECK (
+             (object_type = 'credit_note'     ) OR (object_type = 'invoice'                 ) OR (object_type = 'sales_order'          )
+          OR (object_type = 'sales_quotation' ) OR (object_type = 'sales_delivery_order'    ) OR (object_type = 'request_quotation'    )
+          OR (object_type = 'purchase_order'  ) OR (object_type = 'purchase_delivery_order' ) OR (object_type = 'purchase_invoice'     )
+          OR (object_type = 'vendor'          ) OR (object_type = 'customer'                ) OR (object_type = 'part'                 )
+          OR (object_type = 'gl_transaction'  ) OR (object_type = 'dunning'                 ) OR (object_type = 'dunning1'             )
+          OR (object_type = 'dunning2'        ) OR (object_type = 'dunning3'                ) OR (object_type = 'dunning_orig_invoice' )
+          OR (object_type = 'dunning_invoice' ) OR (object_type = 'draft'                   ) OR (object_type = 'statement'            )
+          OR (object_type = 'shop_image'      ) OR (object_type = 'letter'                  ) OR (object_type = 'project'              )
+          OR (object_type = 'invoice_for_advance_payment') OR (object_type = 'final_invoice')
+  );
diff --git a/sql/Pg-upgrade2/new_chart_1593_1495.sql b/sql/Pg-upgrade2/new_chart_1593_1495.sql
new file mode 100644 (file)
index 0000000..d6f83d7
--- /dev/null
@@ -0,0 +1,37 @@
+-- @tag: new_chart_1593_1495
+-- @description: Neue Konten "Verrechnungskonto erhalt. Anzahl. bei Buchung über Debitorenkonto"
+-- @depends: release_3_5_8
+
+
+DO $$
+BEGIN
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      new_accno text := '1593';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Verrechnungskonto erhalt. Anzahl. bei Buchung über Debitorenkonto','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      new_accno text := '1495';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Verrechnungskonto erhalt. Anzahl. bei Buchung über Debitorenkonto','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+END $$;
diff --git a/sql/Pg-upgrade2/new_chart_3260_1711.sql b/sql/Pg-upgrade2/new_chart_3260_1711.sql
new file mode 100644 (file)
index 0000000..a9fb7d4
--- /dev/null
@@ -0,0 +1,37 @@
+-- @tag: new_chart_3260_1711
+-- @description: Neues Konto "Erhaltene, versteuerte Anzahlungen 7 % USt (Verbindlichkeiten)"
+-- @depends: release_3_5_8
+
+
+DO $$
+BEGIN
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      new_accno text := '1711';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Erhaltene, versteuerte Anzahlungen 7 % USt (Verbindlichkeiten)','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      new_accno text := '3260';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Erhaltene, versteuerte Anzahlungen 7 % USt (Verbindlichkeiten)','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+END $$;
diff --git a/sql/Pg-upgrade2/new_chart_3272_1718.sql b/sql/Pg-upgrade2/new_chart_3272_1718.sql
new file mode 100644 (file)
index 0000000..24c973a
--- /dev/null
@@ -0,0 +1,37 @@
+-- @tag: new_chart_3272_1718
+-- @description: Neues Konto "Erhaltene, versteuerte Anzahlungen 19 % USt (Verbindlichkeiten)"
+-- @depends: release_3_5_8
+
+
+DO $$
+BEGIN
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR03EU' THEN
+    DECLARE
+      new_accno text := '1718';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Erhaltene, versteuerte Anzahlungen 19 % USt (Verbindlichkeiten)','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+  IF ( SELECT coa FROM defaults ) = 'Germany-DATEV-SKR04EU' THEN
+    DECLARE
+      new_accno text := '3272';
+
+    BEGIN
+      IF ( SELECT COUNT(accno) FROM chart WHERE accno LIKE new_accno ) = 0 THEN
+        INSERT INTO chart (accno, description, charttype, category, link, taxkey_id)
+          VALUES (new_accno, 'Erhaltene, versteuerte Anzahlungen 19 % USt (Verbindlichkeiten)','A', 'L', 'AR_amount', 0);
+        INSERT INTO taxkeys (chart_id, tax_id, taxkey_id, startdate)
+          VALUES ((SELECT id FROM chart WHERE accno LIKE new_accno), 0, 0, '1970-01-01');
+      END IF;
+    END;
+  END IF;
+
+END $$;
index 0d2960f..75276e4 100644 (file)
 
 \newcommand{\textUstid} {UStId:}
 
+% anzahlungsrechnung (invoice_for_advance_payment)
+\newcommand{\anzahlungsrechnung} {Anzahlungsrechnung}
+\newcommand{\schlussrechnung} {Schlussrechnung}
+\newcommand{\ust} {USt}
+\newcommand{\abzueglichAnzahlungsrechnungen} {Abzüglich folgender Anzahlungsrechnungen}
+\newcommand{\rechnungsbetrag} {Rechnungsbetrag}
+
 % gutschrift (credit_note)
 \newcommand{\gutschrift} {Gutschrift}
 \newcommand{\fuerRechnung} {für Rechnung}
index a2b6ec3..a6322df 100644 (file)
 
 \newcommand{\textUstid} {VAT number:}
 
+% anzahlungsrechnung (invoice_for_advance_payment)
+\newcommand{\anzahlungsrechnung} {Invoice for advance payment}
+\newcommand{\schlussrechnung} {Final Invoice}
+\newcommand{\ust} {VAT}
+\newcommand{\abzueglichAnzahlungsrechnungen} {Minus following invoices for advance payment}
+\newcommand{\rechnungsbetrag} {Invoice amount}
+
 % gutschrift (credit_note)
 \newcommand{\gutschrift} {Credit note}
 \newcommand{\fuerRechnung} {for invoice}
diff --git a/templates/print/marei/final_invoice.tex b/templates/print/marei/final_invoice.tex
new file mode 120000 (symlink)
index 0000000..b6a6ad8
--- /dev/null
@@ -0,0 +1 @@
+invoice.tex
\ No newline at end of file
index 586847f..7cdef54 100644 (file)
 % settings: Einstellungen, Logo, Briefpapier, Kopfzeile, Fusszeile
 \input{insettings.tex}
 
+<%if template_meta.formname == "invoice_for_advance_payment"%>
+  \renewcommand{\rechnung}{\anzahlungsrechnung}
+<%end if%>
+
+<%if template_meta.formname == "final_invoice"%>
+  \renewcommand{\rechnung}{\schlussrechnung}
+<%end if%>
 
 % laufende Kopfzeile:
 \ourhead{\kundennummer}{<%customernumber%>}{\rechnung}{<%invnumber%>}{<%invdate%>}
 
 \vspace{0.2cm}
 
+<%if iap_existing%>%
+  \abzueglichAnzahlungsrechnungen:\\
+  \begin{SimpleTabular}[colspec=llr<{\tabcurrency}r<{\tabcurrency},headline={\bfseries\nr& \bfseries\date& \bfseries\betrag & \bfseries\ust}]%
+    <%foreach iap_invnumber%>%
+      <%iap_invnumber%> & <%iap_transdate_as_date%> & <%iap_amount%> & <%iap_taxamount%>\\%
+    <%end iap_invnumber%>%
+  \end{SimpleTabular}%
+  <%if template_meta.formname == "final_invoice"%>%
+    \bfseries\rechnungsbetrag: <%iap_final_amount%> \currency\\%
+  <%end%>%
+<%end iap_available%>%
+
 \Ifstr{<%deliverydate%>}{}{%
   \leistungsdatumGleichRechnungsdatum%
 }{
diff --git a/templates/print/marei/invoice_for_advance_payment.tex b/templates/print/marei/invoice_for_advance_payment.tex
new file mode 120000 (symlink)
index 0000000..b6a6ad8
--- /dev/null
@@ -0,0 +1 @@
+invoice.tex
\ No newline at end of file
index 35a8a3a..05f4e9e 100644 (file)
    <td>[% P.chart.picker('defaults.ar_chart_id', SELF.defaults.ar_chart_id, type='AR', choose=1, style=style) %]<td>
   </tr>
 
+  <tr>
+   <td align="right">[% LxERP.t8("Clearing account for advance payments") %]</td>
+   <td>[% P.chart.picker('defaults.advance_payment_clearing_chart_id', SELF.defaults.advance_payment_clearing_chart_id, choose=1, style=style) %]<td>
+  </tr>
+
   <tr>
    <td align="right">[% LxERP.t8("Account for workflow from purchase order to ap transaction") %]</td>
    <td>[% P.chart.picker('defaults.workflow_po_ap_chart_id', SELF.defaults.workflow_po_ap_chart_id, type='AP_amount', choose=1, style=style) %]<td>
index 7d4c608..856c7f4 100644 (file)
     </table>
    </td>
   </tr>
-
-[% PROCESS 'is/_payments.html' %]
+[% IF is_type_normal_invoice OR  is_type_credit_note %]
+  [% PROCESS 'is/_payments.html' %]
+[% END %]
  </table>
 </div>
 [% PROCESS 'webdav/_list.html' %]
index 5b78c3e..ec575dd 100644 (file)
@@ -34,9 +34,8 @@
 [%- END %]
 [%- IF id %]
   [%- IF INSTANCE_CONF.get_doc_storage %]
-  [% object_type = is_type_credit_note? 'credit_note' : 'invoice' %]
-  <li><a href="controller.pl?action=File/list&file_type=document&object_type=[% object_type %]&object_id=[% HTML.url(id) %]">[% 'Documents' | $T8 %]</a></li>
-  <li><a href="controller.pl?action=File/list&file_type=attachment&object_type=[% object_type %]&object_id=[% HTML.url(id) %]">[% 'Attachments' | $T8 %]</a></li>
+  <li><a href="controller.pl?action=File/list&file_type=document&object_type=[% type %]&object_id=[% HTML.url(id) %]">[% 'Documents' | $T8 %]</a></li>
+  <li><a href="controller.pl?action=File/list&file_type=attachment&object_type=[% type %]&object_id=[% HTML.url(id) %]">[% 'Attachments' | $T8 %]</a></li>
   [%- END %]
   [%- IF AUTH.assert('record_links', 1) %]
   <li><a href="controller.pl?action=RecordLinks/ajax_list&object_model=Invoice&object_id=[% HTML.url(id) %]">[% 'Linked Records' | $T8 %]</a></li>