gnucash maint: Multiple changes pushed

Geert Janssens gjanssens at code.gnucash.org
Thu May 24 13:10:11 EDT 2018


Updated	 via  https://github.com/Gnucash/gnucash/commit/5389aa22 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fcabf6bb (commit)
	from  https://github.com/Gnucash/gnucash/commit/2e8df198 (commit)



commit 5389aa22ab8c161eabcba4714e350344cd00478b
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu May 24 18:54:44 2018 +0200

    Update invoice reports to use totals calculate by gncInvoice
    
    This should give a consistent representation of invoice data across the application.

diff --git a/gnucash/report/business-reports/easy-invoice.scm b/gnucash/report/business-reports/easy-invoice.scm
index 985b6eb..ecf14a2 100644
--- a/gnucash/report/business-reports/easy-invoice.scm
+++ b/gnucash/report/business-reports/easy-invoice.scm
@@ -116,18 +116,6 @@
 	(addto! heading-list (_ "Total")))
     (reverse heading-list)))
 
-(define (make-account-hash) (make-hash-table 23))
-
-(define (update-account-hash hash values)
-  (for-each
-   (lambda (item)
-     (let* ((acct (car item))
-	    (val (cdr item))
-	    (ref (hash-ref hash acct)))
-
-       (hash-set! hash acct (if ref (gnc-numeric-add-fixed ref val) val))))
-   values))
-
 (define (monetary-or-percent numeric currency entry-type)
   (if (gnc:entry-type-percent-p entry-type)
       (string-append (gnc:default-html-gnc-numeric-renderer numeric #f) " " (_ "%"))
@@ -368,28 +356,23 @@
 		monetary))))
 
     (define (add-subtotal-row table used-columns
-			      subtotal-collector subtotal-style subtotal-label)
-      (let ((currency-totals (subtotal-collector
-			      'format gnc:make-gnc-monetary #f)))
-
-	(for-each (lambda (currency)
-		    (gnc:html-table-append-row/markup!
-		     table
-		     subtotal-style
-		     (append (cons (gnc:make-html-table-cell/markup
-				    "total-label-cell" subtotal-label)
-				   '())
-			     (list (gnc:make-html-table-cell/size/markup
-				    1 (colspan currency used-columns)
-				    "total-number-cell"
-				    (display-subtotal currency used-columns))))))
-		  currency-totals)))
+                              subtotal subtotal-style subtotal-label)
+      (let ((subtotal-mon (gnc:make-gnc-monetary currency subtotal)))
+
+        (gnc:html-table-append-row/markup!
+            table
+            subtotal-style
+            (append (cons (gnc:make-html-table-cell/markup
+                        "total-label-cell" subtotal-label)
+                        '())
+                    (list (gnc:make-html-table-cell/size/markup
+                        1 (colspan subtotal-mon used-columns)
+                        "total-number-cell"
+                        (display-subtotal subtotal-mon used-columns)))))))
 
     (define (add-payment-row table used-columns split total-collector reverse-payments?)
       (let* ((t (xaccSplitGetParent split))
 	     (currency (xaccTransGetCurrency t))
-	     (invoice (opt-val gnc:pagename-general gnc:optname-invoice-number))
-	     (owner '())
 	     ;; Depending on the document type, the payments may need to be sign-reversed
 	     (amt (gnc:make-gnc-monetary currency
 		    (if reverse-payments?
@@ -422,36 +405,34 @@
 				    table
 				    used-columns
 				    width
-				    odd-row?
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash)
+				    odd-row?)
       (if (null? entries)
-	  (begin
-            ; jamie
-	    (if (opt-val "Display" "Subtotal")
-	       (add-subtotal-row table used-columns value-collector
-			      "grand-total" (_ "Net Price")))
-
-	    (if display-all-taxes
-		(hash-for-each
-		 (lambda (acct value)
-		   (let ((collector (gnc:make-commodity-collector))
-			 (commodity (xaccAccountGetCommodity acct))
-			 (name (xaccAccountGetName acct)))
-		     (collector 'add commodity value)
-		     (add-subtotal-row table used-columns collector
-				       "grand-total" name)))
-		 acct-hash)
-
-		; nope, just show the total tax.
-		(add-subtotal-row table used-columns tax-collector
-				  "grand-total" (_ "Tax")))
-
-	    (add-subtotal-row table used-columns total-collector
-			      "grand-total" (_ "Total Price"))
+          (let ((total-collector (gnc:make-commodity-collector)))
 
+            ; jamie
+            (if (opt-val "Display" "Subtotal")
+                (add-subtotal-row table used-columns (gncInvoiceGetTotalSubtotal invoice)
+                                  "grand-total" (_ "Net Price")))
+
+            (if display-all-taxes
+              (let ((acct-val-list (gncInvoiceGetTotalTaxList invoice)))
+                (for-each
+                  (lambda (parm)
+                    (let* ((value (cdr parm))
+                           (acct (car parm))
+                           (name (xaccAccountGetName acct)))
+                      (add-subtotal-row table used-columns value
+                                        "grand-total" name)))
+                    acct-val-list))
+
+              ; nope, just show the total tax.
+              (add-subtotal-row table used-columns (gncInvoiceGetTotalTax invoice)
+                                "grand-total" (_ "Tax")))
+
+            (add-subtotal-row table used-columns (gncInvoiceGetTotal invoice)
+                              "grand-total" (_ "Total Price"))
+
+            (total-collector 'add currency (gncInvoiceGetTotal invoice))
 	    (if (and show-payments (not (null? lot)))
 		(let ((splits (sort-list!
 			       (gnc-lot-get-split-list lot)
@@ -463,10 +444,11 @@
 		   (lambda (split)
 		     (if (not (equal? (xaccSplitGetParent split) txn))
 			 (add-payment-row table used-columns
-					  split total-collector reverse-payments?)))
+					  split total-collector
+					  reverse-payments?)))
 		   splits)))
 
-	    (add-subtotal-row table used-columns total-collector
+	    (add-subtotal-row table used-columns (cadr (total-collector 'getpair currency #f))
 			      "grand-total" (_ "Amount Due")))
 
 	  ;;
@@ -484,24 +466,6 @@
 					      current-row-style
 					      cust-doc? credit-note?)))
 
-	    (if display-all-taxes
-		(let ((tax-list (gncEntryGetDocTaxValues current cust-doc? credit-note?)))
-		  (update-account-hash acct-hash tax-list))
-		(tax-collector 'add
-			       (gnc:gnc-monetary-commodity (cdr entry-values))
-			       (gnc:gnc-monetary-amount (cdr entry-values))))
-
-	    (value-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (cdr entry-values))
-			     (gnc:gnc-monetary-amount (cdr entry-values)))
-
 	    (let ((order (gncEntryGetOrder current)))
 	      (if (not (null? order)) (add-order order)))
 
@@ -509,17 +473,12 @@
 				    table
 				    used-columns
 				    width
-				    (not odd-row?)
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash))))
+				    (not odd-row?)))))
 
     (let* ((table (gnc:make-html-table))
 	   (used-columns (build-column-used options))
 	   (width (num-columns-required used-columns))
-	   (entries (gncInvoiceGetEntries invoice))
-	   (totals (gnc:make-commodity-collector)))
+	   (entries (gncInvoiceGetEntries invoice)))
 
       (gnc:html-table-set-col-headers!
        table
@@ -529,11 +488,7 @@
 			      table
 			      used-columns
 			      width
-			      #t
-			      (gnc:make-commodity-collector)
-			      (gnc:make-commodity-collector)
-			      totals
-			      (make-account-hash))
+			      #t)
       table)))
 
 (define (string-expand string character replace-string)
diff --git a/gnucash/report/business-reports/fancy-invoice.scm b/gnucash/report/business-reports/fancy-invoice.scm
index 77c2b4e..3e47430 100644
--- a/gnucash/report/business-reports/fancy-invoice.scm
+++ b/gnucash/report/business-reports/fancy-invoice.scm
@@ -134,18 +134,6 @@
 	(addto! heading-list (_ "Total")))
     (reverse heading-list)))
 
-(define (make-account-hash) (make-hash-table 23))
-
-(define (update-account-hash hash values)
-  (for-each
-   (lambda (item)
-     (let* ((acct (car item))
-	    (val (cdr item))
-	    (ref (hash-ref hash acct)))
-
-       (hash-set! hash acct (if ref (gnc-numeric-add-fixed ref val) val))))
-   values))
-
 
 (define (monetary-or-percent numeric currency entry-type)
   (if (gnc:entry-type-percent-p entry-type)
@@ -406,25 +394,19 @@
       )
 
     (define (add-subtotal-row table used-columns
-			      subtotal-collector subtotal-style subtotal-label)
-      (let ((currency-totals (subtotal-collector
-			      'format gnc:make-gnc-monetary #f)))
-
-	(for-each (lambda (currency)
-		    (gnc:html-table-append-row/markup!
-		     table
-		     subtotal-style
-		     ;; oli-custom modified to colspan the subtotal labels
-		     ;; instead of the data fields
-		     (append (cons (gnc:make-html-table-cell/size/markup
-				    1 (colspan currency used-columns)
-				    "total-label-cell" subtotal-label)
-				   '())
-			     (list (gnc:make-html-table-cell/markup
-				    ;; 1 (colspan currency used-columns)
-				    "total-number-cell"
-				    (display-subtotal currency used-columns))))))
-		  currency-totals)))
+                              subtotal subtotal-style subtotal-label)
+      (let ((subtotal-mon (gnc:make-gnc-monetary currency subtotal)))
+
+        (gnc:html-table-append-row/markup!
+            table
+            subtotal-style
+            (append (cons (gnc:make-html-table-cell/markup
+                        "total-label-cell" subtotal-label)
+                        '())
+                    (list (gnc:make-html-table-cell/size/markup
+                        1 (colspan subtotal-mon used-columns)
+                        "total-number-cell"
+                        (display-subtotal subtotal-mon used-columns)))))))
 
     (define (add-payment-row table used-columns split total-collector reverse-payments?)
       (let* ((t (xaccSplitGetParent split))
@@ -463,16 +445,13 @@
 				    table
 				    used-columns
 				    width
-				    odd-row?
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash)
+				    odd-row?)
       (if (null? entries)
-	  (begin
-	    ;; oli-custom - modified to have a minimum of entries per table,
-	    ;; currently defaults to 24
-	    ;; also, doesn't count payment rows and stuff
+        (let ((total-collector (gnc:make-commodity-collector)))
+
+            ;; oli-custom - modified to have a minimum of entries per table,
+            ;; currently defaults to 24
+            ;; also, doesn't count payment rows and stuff
             (do ((entries-added entries-added (+ entries-added 1))
                  (odd-row? odd-row? (not odd-row?)))
               ((> entries-added (opt-val "Display" "Minimum # of entries" )))
@@ -480,29 +459,31 @@
                table (if odd-row? "normal-row" "alternate-row")
                (get-empty-row (num-columns-required used-columns)))
               )
-	    (add-subtotal-row table used-columns value-collector
-			      "grand-total" (_ "Net Price"))
-
-	    (if display-all-taxes
-		(hash-for-each
-		 (lambda (acct value)
-		   (let ((collector (gnc:make-commodity-collector))
-			 (commodity (xaccAccountGetCommodity acct))
-			 (name (xaccAccountGetName acct)))
-		     (collector 'add commodity value)
-		     (add-subtotal-row table used-columns collector
-				       "grand-total" (string-expand
-						      name #\space " "))))
-		 acct-hash)
-
-		; nope, just show the total tax.
-		(add-subtotal-row table used-columns tax-collector
-				  "grand-total" (_ "Tax")))
-
-	    (add-subtotal-row table used-columns total-collector
-			      "grand-total" (string-expand (_ "Total Price")
-							   #\space " "))
 
+            (add-subtotal-row table used-columns (gncInvoiceGetTotalSubtotal invoice)
+                              "grand-total" (_ "Net Price"))
+
+            (if display-all-taxes
+              (let ((acct-val-list (gncInvoiceGetTotalTaxList invoice)))
+                (for-each
+                  (lambda (parm)
+                    (let* ((value (cdr parm))
+                           (acct (car parm))
+                           (name (xaccAccountGetName acct)))
+                      (add-subtotal-row table used-columns value
+                                        "grand-total" (string-expand
+                                                       name #\space " "))))
+                    acct-val-list))
+
+              ; nope, just show the total tax.
+              (add-subtotal-row table used-columns (gncInvoiceGetTotalTax invoice)
+                                "grand-total" (_ "Tax")))
+
+            (add-subtotal-row table used-columns (gncInvoiceGetTotal invoice)
+                              "grand-total" (string-expand (_ "Total Price")
+                                                            #\space " "))
+
+            (total-collector 'add currency (gncInvoiceGetTotal invoice))
 	    (if (and show-payments (not (null? lot)))
 		(let ((splits (sort-list!
 			       (gnc-lot-get-split-list lot)
@@ -518,9 +499,9 @@
 					  reverse-payments?)))
 		   splits)))
 
-	    (add-subtotal-row table used-columns total-collector
+	    (add-subtotal-row table used-columns (cadr (total-collector 'getpair currency #f))
 			      "grand-total" (string-expand (_ "Amount Due")
-							   #\space " ")))
+                                                            #\space " ")))
 
 	  ;;
 	  ;; End of BEGIN -- now here's the code to handle all the entries!
@@ -537,24 +518,6 @@
 					      current-row-style
 					      cust-doc? credit-note?)))
 
-	    (if display-all-taxes
-		(let ((tax-list (gncEntryGetDocTaxValues current cust-doc? credit-note?)))
-		  (update-account-hash acct-hash tax-list))
-		(tax-collector 'add
-			       (gnc:gnc-monetary-commodity (cdr entry-values))
-			       (gnc:gnc-monetary-amount (cdr entry-values))))
-
-	    (value-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (cdr entry-values))
-			     (gnc:gnc-monetary-amount (cdr entry-values)))
-
 	    (let ((order (gncEntryGetOrder current)))
 	      (if (not (null? order)) (add-order order)))
 
@@ -564,17 +527,12 @@
 				    table
 				    used-columns
 				    width
-				    (not odd-row?)
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash))))
+				    (not odd-row?)))))
 
     (let* ((table (gnc:make-html-table))
 	   (used-columns (build-column-used options))
 	   (width (num-columns-required used-columns))
-	   (entries (gncInvoiceGetEntries invoice))
-	   (totals (gnc:make-commodity-collector)))
+	   (entries (gncInvoiceGetEntries invoice)))
 
       (gnc:html-table-set-col-headers!
        table
@@ -584,11 +542,7 @@
 			      table
 			      used-columns
 			      width
-			      #t
-			      (gnc:make-commodity-collector)
-			      (gnc:make-commodity-collector)
-			      totals
-			      (make-account-hash))
+			      #t)
       table)))
 
 (define (string-expand string character replace-string)
diff --git a/gnucash/report/business-reports/invoice.scm b/gnucash/report/business-reports/invoice.scm
index 3434c95..2109115 100644
--- a/gnucash/report/business-reports/invoice.scm
+++ b/gnucash/report/business-reports/invoice.scm
@@ -110,18 +110,6 @@
 	(addto! heading-list (_ "Total")))
     (reverse heading-list)))
 
-(define (make-account-hash) (make-hash-table 23))
-
-(define (update-account-hash hash values)
-  (for-each
-   (lambda (item)
-     (let* ((acct (car item))
-	    (val (cdr item))
-	    (ref (hash-ref hash acct)))
-
-       (hash-set! hash acct (if ref (gnc-numeric-add-fixed ref val) val))))
-   values))
-
 
 (define (monetary-or-percent numeric currency entry-type)
   (if (gnc:entry-type-percent-p entry-type)
@@ -323,10 +311,10 @@
 	(display-all-taxes (opt-val "Display" "Individual Taxes"))
 	(lot (gncInvoiceGetPostedLot invoice))
 	(txn (gncInvoiceGetPostedTxn invoice))
-  (job? (opt-val "Display" "Job Details"))
+        (job? (opt-val "Display" "Job Details"))
 	(currency (gncInvoiceGetCurrency invoice))
-  (jobnumber  (gncJobGetID (gncOwnerGetJob (gncInvoiceGetOwner  invoice))))
-  (jobname    (gncJobGetName (gncOwnerGetJob (gncInvoiceGetOwner  invoice))))
+        (jobnumber  (gncJobGetID (gncOwnerGetJob (gncInvoiceGetOwner  invoice))))
+        (jobname    (gncJobGetName (gncOwnerGetJob (gncInvoiceGetOwner  invoice))))
 	(reverse-payments? (not (gncInvoiceAmountPositive invoice))))
 
     (define (colspan monetary used-columns)
@@ -346,28 +334,23 @@
 		monetary))))
 
     (define (add-subtotal-row table used-columns
-			      subtotal-collector subtotal-style subtotal-label)
-      (let ((currency-totals (subtotal-collector
-			      'format gnc:make-gnc-monetary #f)))
-
-	(for-each (lambda (currency)
-		    (gnc:html-table-append-row/markup!
-		     table
-		     subtotal-style
-		     (append (cons (gnc:make-html-table-cell/markup
-				    "total-label-cell" subtotal-label)
-				   '())
-			     (list (gnc:make-html-table-cell/size/markup
-				    1 (colspan currency used-columns)
-				    "total-number-cell"
-				    (display-subtotal currency used-columns))))))
-		  currency-totals)))
+                              subtotal subtotal-style subtotal-label)
+      (let ((subtotal-mon (gnc:make-gnc-monetary currency subtotal)))
+
+        (gnc:html-table-append-row/markup!
+            table
+            subtotal-style
+            (append (cons (gnc:make-html-table-cell/markup
+                        "total-label-cell" subtotal-label)
+                        '())
+                    (list (gnc:make-html-table-cell/size/markup
+                        1 (colspan subtotal-mon used-columns)
+                        "total-number-cell"
+                        (display-subtotal subtotal-mon used-columns)))))))
 
     (define (add-payment-row table used-columns split total-collector reverse-payments?)
       (let* ((t (xaccSplitGetParent split))
 	     (currency (xaccTransGetCurrency t))
-	     (invoice (opt-val gnc:pagename-general gnc:optname-invoice-number))
-	     (owner '())
 	     ;; Depending on the document type, the payments may need to be sign-reversed
 	     (amt (gnc:make-gnc-monetary currency
 		    (if reverse-payments?
@@ -400,34 +383,32 @@
 				    table
 				    used-columns
 				    width
-				    odd-row?
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash)
+				    odd-row?)
       (if (null? entries)
-	  (begin
-	    (add-subtotal-row table used-columns value-collector
-			      "grand-total" (_ "Net Price"))
-
-	    (if display-all-taxes
-		(hash-for-each
-		 (lambda (acct value)
-		   (let ((collector (gnc:make-commodity-collector))
-			 (commodity (xaccAccountGetCommodity acct))
-			 (name (xaccAccountGetName acct)))
-		     (collector 'add commodity value)
-		     (add-subtotal-row table used-columns collector
-				       "grand-total" name)))
-		 acct-hash)
-
-		; nope, just show the total tax.
-		(add-subtotal-row table used-columns tax-collector
-				  "grand-total" (_ "Tax")))
-
-	    (add-subtotal-row table used-columns total-collector
-			      "grand-total" (_ "Total Price"))
-
+          (let ((total-collector (gnc:make-commodity-collector)))
+
+            (add-subtotal-row table used-columns (gncInvoiceGetTotalSubtotal invoice)
+                              "grand-total" (_ "Net Price"))
+
+            (if display-all-taxes
+              (let ((acct-val-list (gncInvoiceGetTotalTaxList invoice)))
+                (for-each
+                  (lambda (parm)
+                    (let* ((value (cdr parm))
+                           (acct (car parm))
+                           (name (xaccAccountGetName acct)))
+                      (add-subtotal-row table used-columns value
+                                        "grand-total" name)))
+                    acct-val-list))
+
+              ; nope, just show the total tax.
+              (add-subtotal-row table used-columns (gncInvoiceGetTotalTax invoice)
+                                "grand-total" (_ "Tax")))
+
+            (add-subtotal-row table used-columns (gncInvoiceGetTotal invoice)
+                              "grand-total" (_ "Total Price"))
+
+            (total-collector 'add currency (gncInvoiceGetTotal invoice))
 	    (if (and show-payments (not (null? lot)))
 		(let ((splits (sort-list!
 			       (gnc-lot-get-split-list lot)
@@ -443,7 +424,7 @@
 					  reverse-payments?)))
 		   splits)))
 
-	    (add-subtotal-row table used-columns total-collector
+	    (add-subtotal-row table used-columns (cadr (total-collector 'getpair currency #f))
 			      "grand-total" (_ "Amount Due")))
 
 	  ;;
@@ -461,24 +442,6 @@
 					      current-row-style
 					      cust-doc? credit-note?)))
 
-	    (if display-all-taxes
-		(let ((tax-list (gncEntryGetDocTaxValues current cust-doc? credit-note?)))
-		  (update-account-hash acct-hash tax-list))
-		(tax-collector 'add
-			       (gnc:gnc-monetary-commodity (cdr entry-values))
-			       (gnc:gnc-monetary-amount (cdr entry-values))))
-
-	    (value-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (car entry-values))
-			     (gnc:gnc-monetary-amount (car entry-values)))
-	    (total-collector 'add
-			     (gnc:gnc-monetary-commodity (cdr entry-values))
-			     (gnc:gnc-monetary-amount (cdr entry-values)))
-
 	    (let ((order (gncEntryGetOrder current)))
 	      (if (not (null? order)) (add-order order)))
 
@@ -486,17 +449,12 @@
 				    table
 				    used-columns
 				    width
-				    (not odd-row?)
-				    value-collector
-				    tax-collector
-				    total-collector
-				    acct-hash))))
+				    (not odd-row?)))))
 
     (let* ((table (gnc:make-html-table))
 	   (used-columns (build-column-used options))
 	   (width (num-columns-required used-columns))
-	   (entries (gncInvoiceGetEntries invoice))
-	   (totals (gnc:make-commodity-collector)))
+	   (entries (gncInvoiceGetEntries invoice)))
 
       (gnc:html-table-set-col-headers!
        table
@@ -506,11 +464,7 @@
 			      table
 			      used-columns
 			      width
-			      #t
-			      (gnc:make-commodity-collector)
-			      (gnc:make-commodity-collector)
-			      totals
-			      (make-account-hash))
+			      #t)
       table)))
 
 (define (string-expand string character replace-string)
diff --git a/gnucash/report/business-reports/receipt.eguile.scm b/gnucash/report/business-reports/receipt.eguile.scm
index 18c53f2..da34d85 100644
--- a/gnucash/report/business-reports/receipt.eguile.scm
+++ b/gnucash/report/business-reports/receipt.eguile.scm
@@ -170,10 +170,12 @@
 
       <tbody> <!-- display invoice entry lines, keeping running totals -->
         <?scm
-          (let ((tax-total (gnc:make-commodity-collector))
-                (sub-total (gnc:make-commodity-collector))
-                (dsc-total (gnc:make-commodity-collector))
-                (inv-total (gnc:make-commodity-collector)))
+          (let* ((inv-total (gncInvoiceGetTotal opt-invoice))
+                 (tax-total (gncInvoiceGetTotalTax opt-invoice))
+                 (sub-total (gncInvoiceGetTotalSubtotal opt-invoice))
+                 (dsc-total (- inv-total tax-total sub-total))
+                 (total-col (gnc:make-commodity-collector)))
+            (total-col 'add currency inv-total)
             (for entry in entries do
                 (let ((qty       (gncEntryGetQuantity entry))
                       (each      (gncEntryGetInvPrice entry))
@@ -186,11 +188,6 @@
                       (acc       (gncEntryGetInvAccount entry))
                       (taxable   (gncEntryGetInvTaxable entry))
                       (taxtable  (gncEntryGetInvTaxTable entry)))
-                  (inv-total 'add currency rval)
-                  (inv-total 'add currency rtaxval)
-                  (tax-total 'add currency rtaxval)
-                  (sub-total 'add currency rval)
-                  (dsc-total 'add currency rdiscval)
         ?>
         <tr valign="top">
           <td align="left"><?scm:d (qof-print-date (gncEntryGetDate entry)) ?></td>
@@ -227,11 +224,11 @@
         <?scm (if tax? (begin ?>
           <tr valign="top">
             <td align="left"  class="subtotal" colspan="<?scm:d (- maxcols 2) ?>"><strong><?scm:d opt-net-price-heading ?></strong></td>
-            <td align="right" class="subtotal" colspan="2>"><strong><?scm (display-comm-coll-total sub-total #f) ?></strong></td>
+            <td align="right" class="subtotal" colspan="2>"><strong><?scm:d (fmtmoney currency sub-total) ?></strong></td>
           </tr>
           <tr valign="top">
             <td align="left"  class="subtotal" colspan="<?scm:d (- maxcols 2) ?>"><strong><?scm:d opt-tax-amount-heading ?></strong></td>
-            <td align="right" class="subtotal" colspan="2>"><strong><?scm (display-comm-coll-total tax-total #f) ?></strong></td>
+            <td align="right" class="subtotal" colspan="2>"><strong><?scm:d (fmtmoney currency tax-total) ?></strong></td>
           </tr>
         <?scm )) ?>
 
@@ -240,7 +237,7 @@
         <?scm (if payments? (begin ?>
           <tr valign="top">
             <td align="left"  class="subtotal" colspan="<?scm:d (- maxcols 2) ?>"><strong><?scm:d opt-total-price-heading ?></strong></td>
-            <td align="right" class="subtotal" colspan="2>"><strong><?scm (display-comm-coll-total inv-total #f) ?></strong></td>
+            <td align="right" class="subtotal" colspan="2>"><strong><?scm:d (fmtmoney currency inv-total) ?></strong></td>
           </tr>
         <?scm )) ?>
 
@@ -251,7 +248,7 @@
                   (if (not (equal? t txn)) ; don't process the entry itself as a split
                     (let ((c (xaccTransGetCurrency t))
                           (a (xaccSplitGetValue    split)))
-                      (inv-total 'add c a)
+                      (total-col 'add c a)
         ?>
         <tr valign="top">
           <td align="center"><?scm:d (qof-print-date (xaccTransGetDate t)) ?></td>
@@ -263,7 +260,7 @@
         <!-- total row -->
         <tr valign="top">
           <td align="left"  class="total total_last" colspan="<?scm:d (- maxcols 2) ?>"><strong><?scm:d opt-amount-due-heading ?></strong></td>
-          <td align="right" class="total total_last" colspan="2>"><strong><?scm (display-comm-coll-total inv-total #f) ?></strong></td>
+          <td align="right" class="total total_last" colspan="2>"><strong><?scm (display-comm-coll-total total-col #f) ?></strong></td>
         </tr>
 
       </tbody>
diff --git a/gnucash/report/business-reports/taxinvoice.eguile.scm b/gnucash/report/business-reports/taxinvoice.eguile.scm
index a76fd50..e3629c9 100644
--- a/gnucash/report/business-reports/taxinvoice.eguile.scm
+++ b/gnucash/report/business-reports/taxinvoice.eguile.scm
@@ -324,10 +324,12 @@
 
   <tbody> <!-- display invoice entry lines, keeping running totals -->
     <?scm
-      (let ((tax-total (gnc:make-commodity-collector))
-            (sub-total (gnc:make-commodity-collector))
-            (dsc-total (gnc:make-commodity-collector))
-            (inv-total (gnc:make-commodity-collector)))
+      (let* ((inv-total (gncInvoiceGetTotal opt-invoice))
+             (tax-total (gncInvoiceGetTotalTax opt-invoice))
+             (sub-total (gncInvoiceGetTotalSubtotal opt-invoice))
+             (dsc-total (- inv-total tax-total sub-total))
+             (total-col (gnc:make-commodity-collector)))
+        (total-col 'add currency inv-total)
         (for entry in entries do
             (let ((qty       (gncEntryGetDocQuantity entry credit-note?))
                   (each      (gncEntryGetPrice entry cust-doc? opt-netprice))
@@ -340,11 +342,6 @@
                   (acc       (if cust-doc? (gncEntryGetInvAccount entry)(gncEntryGetBillAccount entry)))
                   (taxable   (if cust-doc? (gncEntryGetInvTaxable entry)(gncEntryGetBillTaxable entry)))
                   (taxtable  (if cust-doc? (gncEntryGetInvTaxTable entry)(gncEntryGetBillTaxTable entry))))
-              (inv-total 'add currency rval)
-              (inv-total 'add currency rtaxval)
-              (tax-total 'add currency rtaxval)
-              (sub-total 'add currency rval)
-              (dsc-total 'add currency rdiscval) ;'
     ?>
     <tr valign="top">
       <?scm (if opt-col-date (begin ?>
@@ -392,16 +389,16 @@
                     (if (and discount?) 1 0)
         ) ?>"><strong><?scm:d opt-subtotal-heading ?></strong></td>
         <?scm (if discount? (begin ?>
-          <td align="right" class="subtotal"><strong><?scm (display-comm-coll-total dsc-total #f) ?></strong></td>
+        <td align="right" class="subtotal"><strong><?scm:d (fmtmoney currency dsc-total) ?></strong></td>
         <?scm )) ?>
         <?scm (if (and tax? taxtables?) (begin ?>
-          <td align="right" class="subtotal"><strong><?scm (display-comm-coll-total sub-total #f) ?></strong></td>
+          <td align="right" class="subtotal"><strong><?scm:d (fmtmoney currency sub-total) ?></strong></td>
           <?scm (if opt-col-taxrate (begin ?>
           <td> </td>
           <?scm )) ?>
-          <td align="right" class="subtotal"><strong><?scm (display-comm-coll-total tax-total #f) ?></strong></td>
+          <td align="right" class="subtotal"><strong><?scm:d (fmtmoney currency tax-total) ?></strong></td>
         <?scm )) ?>
-        <td align="right" class="subtotal"><strong><?scm (display-comm-coll-total inv-total #f) ?></strong></td>
+        <td align="right" class="subtotal"><strong><?scm:d (fmtmoney currency inv-total) ?></strong></td>
       </tr>
     <?scm )) ?>
 
@@ -416,7 +413,7 @@
                     (gnc-numeric-neg(xaccSplitGetValue split))
                     (xaccSplitGetValue split)))
                 )
-                  (inv-total 'add c a) ;'
+                  (total-col 'add c a) ;'
     ?>
     <tr valign="top">
       <?scm (if opt-col-date (begin ?>
@@ -432,7 +429,7 @@
       <td align="left" class="total" colspan="<?scm:d (+ tbl_cols 1) ?>"><strong>
         <?scm:d opt-amount-due-heading ?><?scm (if (not (string=? (gnc-commodity-get-mnemonic opt-report-currency) "")) (begin ?>,
         <?scm:d (gnc-commodity-get-mnemonic opt-report-currency) ?><?scm )) ?></strong></td>
-      <td align="right" class="total"><strong><?scm (display-comm-coll-total inv-total #f) ?></strong></td>
+      <td align="right" class="total"><strong><?scm (display-comm-coll-total total-col #f) ?></strong></td>
     </tr>
 
   </tbody>

commit fcabf6bb9661e4566d3da4b1c30ca8c0e1e2a75b
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu May 24 18:53:15 2018 +0200

    A more detailed revision of gncEntry and gncInvoice related rounding
    
    First change is to ensure gncEntry rounding is consistent. Internally
    calculated values in the entry are never rounded. Consumers of
    gncEntry's calculated values can request them either rounded or not.
    
    Next use a pragmatical approach for calculating values on invoices based on
    the entry values: do the rounding such that we never
    create an unbalanced transaction while posting
    That means
    - round each entry's net value before summing them in net total
    - accumulate all tax totals on invoice level per tax account before rounding
      and round before before summing them in a global tax total
    
    Hopefully this will catch a few more rounding issues in this area.
    
    A complete solution can only offered if we allow users to manually correct
    tax entries. This requires changes to user interface and data format
    so that's not going to happen in gnucash 3.x.

diff --git a/gnucash/register/ledger-core/gncEntryLedger.c b/gnucash/register/ledger-core/gncEntryLedger.c
index 645e809..796667d 100644
--- a/gnucash/register/ledger-core/gncEntryLedger.c
+++ b/gnucash/register/ledger-core/gncEntryLedger.c
@@ -727,6 +727,7 @@ gnc_entry_ledger_compute_value (GncEntryLedger *ledger,
     GncTaxTable *table;
     GList *taxes = NULL;
     int denom = 100;
+    gnc_numeric value_unrounded, taxes_unrounded;
 
     gnc_entry_ledger_get_numeric (ledger, ENTRY_QTY_CELL, &qty);
     gnc_entry_ledger_get_numeric (ledger, ENTRY_PRIC_CELL, &price);
@@ -778,12 +779,18 @@ gnc_entry_ledger_compute_value (GncEntryLedger *ledger,
     }
 
     gncEntryComputeValue (qty, price, (taxable ? table : NULL), taxincluded,
-                          discount, disc_type, disc_how, denom,
-                          value, NULL, &taxes);
+                          discount, disc_type, disc_how, 0,
+                          &value_unrounded, NULL, &taxes);
+
+    if (value)
+        *value = gnc_numeric_convert (value_unrounded, denom,
+                                      GNC_HOW_RND_ROUND_HALF_UP);
 
     /* return the tax value */
+    taxes_unrounded = gncAccountValueTotal (taxes);
     if (tax_value)
-        *tax_value = gncAccountValueTotal (taxes);
+        *tax_value = gnc_numeric_convert (taxes_unrounded, denom,
+                                          GNC_HOW_RND_ROUND_HALF_UP);
 }
 
 gboolean
diff --git a/libgnucash/engine/gncEntry.c b/libgnucash/engine/gncEntry.c
index f850b2a..a47fd3b 100644
--- a/libgnucash/engine/gncEntry.c
+++ b/libgnucash/engine/gncEntry.c
@@ -1084,15 +1084,17 @@ GncOrder * gncEntryGetOrder (const GncEntry *entry)
  * the amount the merchant gets; the taxes are the amount the gov't
  * gets, and the customer pays the sum or value + taxes.
  *
- * The SCU is the denominator to convert the value.
- *
  * The discount return value is just for entertainment -- you may want
  * to let a consumer know how much they saved.
+ *
+ * Note this function will not do any rounding unless forced to prevent overflow.
+ * It's the caller's responsability to round to the proper commodity
+ * denominator if needed.
  */
 static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
 				    const GncTaxTable *tax_table, gboolean tax_included,
 				    gnc_numeric discount, GncAmountType discount_type,
-				    GncDiscountHow discount_how, int SCU,
+				    GncDiscountHow discount_how,
 				    gnc_numeric *value, gnc_numeric *discount_value,
 				    GList **tax_value, gnc_numeric *net_price)
 {
@@ -1110,7 +1112,7 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
 
     /* Step 1: compute the aggregate price */
 
-    aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD | GNC_HOW_RND_ROUND);
+    aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
 
     /* Step 2: compute the pre-tax aggregate */
 
@@ -1154,10 +1156,10 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
                                   gnc_numeric_add (tpercent,
                                           gnc_numeric_create (1, 1),
                                           GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
-                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
 	if (!gnc_numeric_zero_p(qty))
 	{
-	  i_net_price = gnc_numeric_div (pretax, qty, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+	  i_net_price = gnc_numeric_div (pretax, qty, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
 	}
     }
     else
@@ -1195,7 +1197,7 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
                                         GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
             discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
-                                        GNC_HOW_DENOM_LCD);
+                                        GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
         }
 
         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
@@ -1212,14 +1214,14 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
         {
             gnc_numeric after_tax;
 
-            tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
             after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
             after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
                                          GNC_HOW_DENOM_LCD);
             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
                                         GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
             discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
-                                        GNC_HOW_DENOM_LCD);
+                                        GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
         }
 
         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
@@ -1238,16 +1240,10 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
      */
 
     if (discount_value != NULL)
-    {
-        if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND_HALF_UP);
         *discount_value = discount;
-    }
 
     if (value != NULL)
-    {
-        if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND_HALF_UP);
         *value = result;
-    }
 
     /* Now... Compute the list of tax values (if the caller wants it) */
 
@@ -1266,14 +1262,12 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
             switch (gncTaxTableEntryGetType (entry))
             {
             case GNC_AMT_TYPE_VALUE:
-                if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND_HALF_UP);
                 taxes = gncAccountValueAdd (taxes, acc, amount);
                 break;
             case GNC_AMT_TYPE_PERCENT:
                 amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
                                           GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
-                tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-                if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND_HALF_UP);
+                tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
                 taxes = gncAccountValueAdd (taxes, acc, tax);
                 break;
             default:
@@ -1284,10 +1278,7 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
     }
 
     if (net_price != NULL)
-    {
-      if (SCU) i_net_price = gnc_numeric_convert(i_net_price, SCU, GNC_HOW_RND_ROUND_HALF_UP);
       *net_price = i_net_price;
-    }
 
     return;
 }
@@ -1295,12 +1286,12 @@ static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
 void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
                            const GncTaxTable *tax_table, gboolean tax_included,
                            gnc_numeric discount, GncAmountType discount_type,
-                           GncDiscountHow discount_how, int SCU,
+                           GncDiscountHow discount_how, G_GNUC_UNUSED int SCU,
                            gnc_numeric *value, gnc_numeric *discount_value,
                            GList **tax_value)
 {
   gncEntryComputeValueInt (qty, price, tax_table, tax_included, discount, discount_type,
-			   discount_how, SCU, value, discount_value, tax_value, NULL);
+			   discount_how, value, discount_value, tax_value, NULL);
 }
 
 static int
@@ -1328,6 +1319,7 @@ static void
 gncEntryRecomputeValues (GncEntry *entry)
 {
     int denom;
+    GList *tv_iter;
 
     /* See if either tax table changed since we last computed values */
     if (entry->i_tax_table)
@@ -1386,18 +1378,28 @@ gncEntryRecomputeValues (GncEntry *entry)
                           &(entry->b_value), NULL, &(entry->b_tax_values));
 
     entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
-                             GNC_HOW_RND_ROUND_HALF_UP);
+                             GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
-                                  GNC_HOW_RND_ROUND_HALF_UP);
+                                  GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
-    entry->i_tax_value_rounded = gnc_numeric_convert (entry->i_tax_value, denom,
-                                 GNC_HOW_RND_ROUND_HALF_UP);
+    entry->i_tax_value_rounded = gnc_numeric_zero();
+    for (tv_iter = entry->i_tax_values; tv_iter; tv_iter=tv_iter->next)
+    {
+        GncAccountValue *acc_val = tv_iter->data;
+        entry->i_tax_value_rounded = gnc_numeric_add (entry->i_tax_value_rounded, acc_val->value,
+                                     denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+    }
 
     entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
-                             GNC_HOW_RND_ROUND_HALF_UP);
+                             GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
-    entry->b_tax_value_rounded = gnc_numeric_convert (entry->b_tax_value, denom,
-                                 GNC_HOW_RND_ROUND_HALF_UP);
+    entry->b_tax_value_rounded = gnc_numeric_zero();
+    for (tv_iter = entry->b_tax_values; tv_iter; tv_iter=tv_iter->next)
+    {
+        GncAccountValue *acc_val = tv_iter->data;
+        entry->b_tax_value_rounded = gnc_numeric_add (entry->b_tax_value_rounded, acc_val->value,
+                                                      denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+    }
     entry->values_dirty = FALSE;
 }
 
@@ -1441,6 +1443,10 @@ static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round,
         return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero());
 }
 
+/* Note contrary to the GetDoc*Value and GetBal*Value functions below
+ * this function will always round the net price to the entry's
+ * currency denominator (being the invoice/bill denom or 100000 if not
+ * included in a bill or invoice) */
 gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean net)
 {
     gnc_numeric result;
@@ -1448,10 +1454,8 @@ gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean
 
     if (!entry) return gnc_numeric_zero();
     if (!net) return (cust_doc ? entry->i_price : entry->b_price);
-	
-    /* Determine the commodity denominator */
-    denom = get_entry_commodity_denom (entry);
-      
+
+
     /* Compute the net price */
     if (cust_doc)
         gncEntryComputeValueInt (entry->quantity, entry->i_price,
@@ -1459,16 +1463,20 @@ gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean
                                  entry->i_taxincluded,
                                  entry->i_discount, entry->i_disc_type,
                                  entry->i_disc_how,
-                                 denom,
                                  NULL, NULL, NULL, &result);
     else
         gncEntryComputeValueInt (entry->quantity, entry->b_price,
                                  (entry->b_taxable ? entry->b_tax_table : NULL),
                                  entry->b_taxincluded,
                                  gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
-                                 denom,
                                  NULL, NULL, NULL, &result);
 
+    /* Determine the commodity denominator */
+    denom = get_entry_commodity_denom (entry);
+
+    result = gnc_numeric_convert (result, denom,
+                                  GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+
     return result;
 }
 
diff --git a/libgnucash/engine/gncEntry.h b/libgnucash/engine/gncEntry.h
index f555d20..a45d83b 100644
--- a/libgnucash/engine/gncEntry.h
+++ b/libgnucash/engine/gncEntry.h
@@ -47,6 +47,8 @@ typedef enum
     GNC_DISC_POSTTAX
 } GncDiscountHow;
 
+typedef GList AccountValueList;
+
 #ifdef GNUCASH_MAJOR_VERSION
 #include "gncBusiness.h"
 #endif
@@ -243,7 +245,6 @@ void gncEntryCopy (const GncEntry *src, GncEntry *dest, gboolean add_entry);
  * these functions.
  @{
 */
-typedef GList AccountValueList;
 gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn);
 gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn);
 /** Careful: the returned list is NOT owned by the entry and should be freed by the caller */
diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index 298a668..4950204 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -857,15 +857,39 @@ GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)
 }
 
 static gnc_numeric
-gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value,
-                            gboolean use_tax,
-                            gboolean use_payment_type, GncEntryPaymentType type)
+gncInvoiceSumTaxesInternal (AccountValueList *taxes)
+{
+    gnc_numeric tt = gnc_numeric_zero();
+
+    if (taxes)
+    {
+        GList *node;
+        // Note we can use GNC_DENOM_AUTO below for rounding because
+        // the values passed to this function should already have been rounded
+        // to the desired denom and addition will just preserve it in that case.
+        for (node = taxes; node; node=node->next)
+        {
+            GncAccountValue *acc_val = node->data;
+            tt = gnc_numeric_add (tt, acc_val->value, GNC_DENOM_AUTO,
+                                  GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+        }
+    }
+    return tt;
+}
+
+static gnc_numeric
+gncInvoiceGetNetAndTaxesInternal (GncInvoice *invoice, gboolean use_value,
+                            AccountValueList **taxes,
+                            gboolean use_payment_type, GncEntryPaymentType type
+                           )
 {
     GList *node;
-    gnc_numeric total = gnc_numeric_zero();
+    gnc_numeric net_total = gnc_numeric_zero();
     gboolean is_cust_doc, is_cn;
+    AccountValueList *tv_list = NULL;
+    int denom = gnc_commodity_get_fraction(gncInvoiceGetCurrency(invoice));
 
-    g_return_val_if_fail (invoice, total);
+    g_return_val_if_fail (invoice, net_total);
 
     /* Is the current document an invoice/credit note related to a customer or a vendor/employee ?
      * The GncEntry code needs to know to return the proper entry amounts
@@ -873,6 +897,7 @@ gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value,
     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
     is_cn = gncInvoiceGetIsCreditNote (invoice);
 
+
     for (node = gncInvoiceGetEntries(invoice); node; node = node->next)
     {
         GncEntry *entry = node->data;
@@ -881,23 +906,64 @@ gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value,
         if (use_payment_type && gncEntryGetBillPayment (entry) != type)
             continue;
 
-        value = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
-        if (gnc_numeric_check (value) == GNC_ERROR_OK)
+        if (use_value)
+        {
+            // Always use rounded net values to prevent creating imbalanced transactions on posting
+            // https://bugzilla.gnome.org/show_bug.cgi?id=628903
+            value = gncEntryGetDocValue (entry, TRUE, is_cust_doc, is_cn);
+            if (gnc_numeric_check (value) == GNC_ERROR_OK)
+                net_total = gnc_numeric_add (net_total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            else
+                g_warning ("bad value in our entry");
+        }
+
+        if (taxes)
         {
-            if (use_value)
-                total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            AccountValueList *entrytaxes = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
+            tv_list = gncAccountValueAddList (tv_list, entrytaxes);
+            gncAccountValueDestroy (entrytaxes);
         }
-        else
-            g_warning ("bad value in our entry");
+    }
 
-        if (use_tax)
+    if (taxes)
+    {
+        GList *node;
+        // Round tax totals (accumulated per tax account) to prevent creating imbalanced transactions on posting
+        // which could otherwise happen when using a tax table with multiple tax rates
+        for (node = tv_list; node; node=node->next)
         {
-            tax = gncEntryGetDocTaxValue (entry, FALSE, is_cust_doc, is_cn);
-            if (gnc_numeric_check (tax) == GNC_ERROR_OK)
-                total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-            else
-                g_warning ("bad tax-value in our entry");
+            GncAccountValue *acc_val = node->data;
+            acc_val->value = gnc_numeric_convert (acc_val->value,
+                                  denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
         }
+        *taxes = tv_list;
+    }
+
+    return net_total;
+}
+
+static gnc_numeric
+gncInvoiceGetTotalInternal(GncInvoice *invoice, gboolean use_value,
+                           gboolean use_tax,
+                           gboolean use_payment_type, GncEntryPaymentType type)
+{
+    AccountValueList *taxes;
+    gnc_numeric total;
+    int denom;
+
+    if (!invoice) return gnc_numeric_zero();
+
+    denom = gnc_commodity_get_fraction(gncInvoiceGetCurrency(invoice));
+    total = gncInvoiceGetNetAndTaxesInternal(invoice, use_value, use_tax? &taxes : NULL, use_payment_type, type);
+
+    if (use_tax)
+    {
+        // Note we can use GNC_DENOM_AUTO below for rounding because
+        // the values passed to this function should already have been rounded
+        // to the desired denom and addition will just preserve it in that case.
+        total = gnc_numeric_add (total, gncInvoiceSumTaxesInternal (taxes),
+                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+        gncAccountValueDestroy (taxes);
     }
     return total;
 }
@@ -926,6 +992,16 @@ gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type)
     return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, TRUE, type);
 }
 
+AccountValueList *gncInvoiceGetTotalTaxList (GncInvoice *invoice)
+{
+    gnc_numeric unused;
+    AccountValueList *taxes;
+    if (!invoice) return NULL;
+
+    unused = gncInvoiceGetNetAndTaxesInternal(invoice, FALSE, &taxes, FALSE, 0);
+    return taxes;
+}
+
 GList * gncInvoiceGetTypeListForOwnerType (GncOwnerType type)
 {
     GList *type_list = NULL;
@@ -1376,6 +1452,8 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
     char *lot_title;
     Account *ccard_acct = NULL;
     const GncOwner *owner;
+    int denom = xaccAccountGetCommoditySCU(acc);
+    AccountValueList *taxes;
 
     if (!invoice || !acc) return NULL;
     if (gncInvoiceIsPosted (invoice)) return NULL;
@@ -1427,14 +1505,29 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
 
     xaccTransSetDateDue (txn, due_date);
 
+    /* Get invoice total and taxes. */
+    total = gncInvoiceGetTotal (invoice);
+    taxes = gncInvoiceGetTotalTaxList (invoice);
+    /* The two functions above return signs relative to the document
+     * We need to convert them to balance values before we can use them here */
+    if (is_cust_doc)
+    {
+        GList *node;
+        total = gnc_numeric_neg (total);
+        for (node = taxes; node; node = node->next)
+        {
+            GncAccountValue *acc_val = node->data;
+            acc_val->value = gnc_numeric_neg (acc_val->value);
+        }
+    }
+
     /* Iterate through the entries; sum up everything for each account.
      * then create the appropriate splits in this txn.
      */
-    total = gnc_numeric_zero();
+
     for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next)
     {
         gnc_numeric value, tax;
-        GList *taxes;
         GncEntry * entry = iter->data;
         Account *this_acc;
 
@@ -1458,10 +1551,10 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
         }
         gncEntryCommitEdit (entry);
 
-        /* Obtain the Entry's Value and TaxValues */
-        value = gncEntryGetBalValue (entry, FALSE, is_cust_doc);
-        tax   = gncEntryGetBalTaxValue (entry, FALSE, is_cust_doc);
-        taxes = gncEntryGetBalTaxValues (entry, is_cust_doc);
+        /* Obtain the Entry's Value and TaxValues
+           Note we use rounded values here and below to prevent creating an imbalanced transaction */
+        value = gncEntryGetBalValue (entry, TRUE, is_cust_doc);
+        tax   = gncEntryGetBalTaxValue (entry, TRUE, is_cust_doc);
 
         /* add the value for the account split */
         this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
@@ -1472,6 +1565,7 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
             {
                 if (accumulatesplits)
                     splitinfo = gncAccountValueAdd (splitinfo, this_acc, value);
+                    /* Adding to total in case of accumulatesplits will be deferred to later when each split is effectively added */
                 else if (!gncInvoicePostAddSplit (book, this_acc, txn, value,
                                                   gncEntryGetDescription (entry),
                                                   type, invoice))
@@ -1484,7 +1578,7 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
                 }
 
                 /* If there is a credit-card account, and this is a CCard
-                 * payment type, the don't add it to the total, and instead
+                 * payment type, subtract it from the total, and instead
                  * create a split to the CC Acct with a memo of the entry
                  * description instead of the provided memo.  Note that the
                  * value reversal is the same as the post account.
@@ -1496,6 +1590,9 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
                 {
                     Split *split;
 
+                    total = gnc_numeric_sub (total, value, denom,
+                                             GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
+
                     split = xaccMallocSplit (book);
                     xaccSplitSetMemo (split, gncEntryGetDescription (entry));
                     /* set action based on book option */
@@ -1508,30 +1605,30 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
                                            invoice->currency);
 
                 }
-                else
-                    total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
 
             }
             else
                 g_warning ("bad value in our entry");
         }
 
-        /* now merge in the TaxValues */
-        splitinfo = gncAccountValueAddList (splitinfo, taxes);
-
-        /* ... and add the tax total */
-        if (gnc_numeric_check (tax) == GNC_ERROR_OK)
-            total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        else
+        /* check the taxes */
+        if (gnc_numeric_check (tax) != GNC_ERROR_OK)
             g_warning ("bad tax in our entry");
 
-        gncAccountValueDestroy (taxes);
     } /* for */
 
+
+    /* now merge in the TaxValues */
+    splitinfo = gncAccountValueAddList (splitinfo, taxes);
+    gncAccountValueDestroy (taxes);
+
     /* Iterate through the splitinfo list and generate the splits */
     for (iter = splitinfo; iter; iter = iter->next)
     {
         GncAccountValue *acc_val = iter->data;
+
+        //gnc_numeric amt_rounded = gnc_numeric_convert(acc_val->value,
+        //    denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
         if (!gncInvoicePostAddSplit (book, acc_val->account, txn, acc_val->value,
                                      memo, type, invoice))
         {
@@ -1567,8 +1664,8 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
         xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount),
                                invoice->currency);
 
-        total = gnc_numeric_sub (total, to_charge_bal_amount,
-                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        total = gnc_numeric_sub (total, to_charge_bal_amount, denom,
+                                 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     }
 
     /* Now create the Posted split (which is the opposite sign of the above splits) */
diff --git a/libgnucash/engine/gncInvoice.h b/libgnucash/engine/gncInvoice.h
index 1b5b65a..fa9eedc 100644
--- a/libgnucash/engine/gncInvoice.h
+++ b/libgnucash/engine/gncInvoice.h
@@ -164,6 +164,9 @@ gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice);
 gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type);
 gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice);
 gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice);
+/** Return a list of tax totals accumulated per tax account.
+ */
+AccountValueList *gncInvoiceGetTotalTaxList (GncInvoice *invoice);
 
 typedef GList EntryList;
 EntryList * gncInvoiceGetEntries (GncInvoice *invoice);
diff --git a/libgnucash/engine/gncTaxTable.c b/libgnucash/engine/gncTaxTable.c
index 148a4c0..f691f70 100644
--- a/libgnucash/engine/gncTaxTable.c
+++ b/libgnucash/engine/gncTaxTable.c
@@ -936,7 +936,7 @@ GList *gncAccountValueAdd (GList *list, Account *acc, gnc_numeric value)
         if (res->account == acc)
         {
             res->value = gnc_numeric_add (res->value, value, GNC_DENOM_AUTO,
-                                          GNC_HOW_DENOM_LCD);
+                                          GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND_HALF_UP);
             return list;
         }
     }
@@ -970,7 +970,7 @@ gnc_numeric gncAccountValueTotal (GList *list)
     for ( ; list ; list = list->next)
     {
         GncAccountValue *val = list->data;
-        total = gnc_numeric_add (total, val->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        total = gnc_numeric_add (total, val->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND_HALF_UP);
     }
     return total;
 }
diff --git a/libgnucash/engine/test/utest-Entry.c b/libgnucash/engine/test/utest-Entry.c
index 102b269..b8c5361 100644
--- a/libgnucash/engine/test/utest-Entry.c
+++ b/libgnucash/engine/test/utest-Entry.c
@@ -25,6 +25,7 @@
 #include <qof.h>
 #include <unittest-support.h>
 #include "../gncEntry.h"
+#include "../gncTaxTableP.h"
 
 static const gchar *suitename = "/engine/gncEntry";
 void test_suite_gncEntry ( void );
@@ -33,9 +34,11 @@ typedef struct
 {
     QofBook *book;
     Account *account;
+    Account *vatacct;
     GncOwner owner;
     GncCustomer *customer;
     gnc_commodity *commodity;
+    GncInvoice *invoice;
 } Fixture;
 
 static void
@@ -47,6 +50,11 @@ setup( Fixture *fixture, gconstpointer pData )
     fixture->commodity = gnc_commodity_new(fixture->book, "foo", "bar", "xy", "xy", 100);
     xaccAccountSetCommodity(fixture->account, fixture->commodity);
 
+    fixture->vatacct = xaccMallocAccount(fixture->book);
+    xaccAccountSetCommodity(fixture->vatacct, fixture->commodity);
+
+    fixture->invoice = gncInvoiceCreate(fixture->book);
+    gncInvoiceSetCurrency(fixture->invoice, fixture->commodity);
 }
 
 static void
@@ -54,8 +62,13 @@ teardown( Fixture *fixture, gconstpointer pData )
 {
     xaccAccountBeginEdit(fixture->account);
     xaccAccountDestroy(fixture->account);
-    gnc_commodity_destroy(fixture->commodity);
+    xaccAccountBeginEdit(fixture->vatacct);
+    xaccAccountDestroy(fixture->vatacct);
+
+    gncInvoiceBeginEdit(fixture->invoice);
+    gncInvoiceDestroy(fixture->invoice);
 
+    gnc_commodity_destroy(fixture->commodity);
     qof_book_destroy( fixture->book );
 }
 
@@ -109,8 +122,168 @@ test_entry_basics ( Fixture *fixture, gconstpointer pData )
 
 }
 
+static void
+test_entry_rounding ( Fixture *fixture, gconstpointer pData )
+{
+    GncEntry *entry = gncEntryCreate(fixture->book);
+    GncTaxTable *taxtable;
+    GncTaxTableEntry *tt_entry;
+
+    gncTaxTableRegister();
+    taxtable = gncTaxTableCreate(fixture->book);
+    tt_entry = gncTaxTableEntryCreate();
+    gncTaxTableSetName(taxtable, "Percent tax");
+    gncTaxTableEntrySetAccount(tt_entry, fixture->vatacct);
+    gncTaxTableEntrySetType(tt_entry, GNC_AMT_TYPE_PERCENT);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(1000000, 100000));
+    gncTaxTableAddEntry(taxtable, tt_entry);
+
+    // 1. Freestanding entry - a default denominator of 100000 is expected during rounding
+
+    // Test with numbers that don't require rounding
+    /* Tax 10% (high precision GncNumeric), tax not included */
+    gncEntrySetInvTaxable(entry, TRUE);
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    gncEntrySetQuantity(entry, gnc_numeric_create (2, 1));
+    gncEntrySetInvPrice(entry, gnc_numeric_create (3, 1));
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (6, 1)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (6, 10)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (6, 1)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (6, 10)));
+
+    // Test with numbers that do require rounding
+    /* Tax 10% (high precision GncNumeric), tax included */
+    gncEntrySetInvTaxIncluded(entry, TRUE);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 11)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 110)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (545455, 100000)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (54545, 100000)));
+
+    // Use different taxtable percentage precision
+    /* Tax 10% (low precision GncNumeric), tax included */
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(10, 1));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 11)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 110)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (545455, 100000)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (54545, 100000)));
+
+    // Test with odd tax percentage (Taken from a mailing list example)
+    /* Tax 13% (high precision GncNumeric), tax not included */
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(1300000, 100000));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    gncEntrySetQuantity(entry, gnc_numeric_create (1, 1));
+    gncEntrySetInvPrice(entry, gnc_numeric_create (27750, 100));
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Note on the above expected result: the standard gncEntry denom is 100000 if the entry has no invoice or
+     * bill set. So with the example above no rounding is required yet */
+
+    // Test with odd tax percentage (Taken from a mailing list example)
+    /* Tax 13% (low precision GncNumeric), tax not included */
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(13, 1));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Note on the above expected result: the standard gncEntry denom is 100000 if the entry has no invoice or
+     * bill set. So with the example above no rounding is required yet */
+
+    // 2. gncEntry as part of a gncInvoice - the invoice currency's denominator is expected during rounding
+    gncInvoiceAddEntry(fixture->invoice, entry);
+
+    // Test with numbers that don't require rounding
+    /* Tax 10% (high precision GncNumeric), tax not included */
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(1000000, 100000));
+    gncEntrySetQuantity(entry, gnc_numeric_create (2, 1));
+    gncEntrySetInvPrice(entry, gnc_numeric_create (3, 1));
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (6, 1)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (6, 10)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (6, 1)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (6, 10)));
+
+    // Test with numbers that do require rounding
+    /* Tax 10% (high precision GncNumeric), tax included */
+    gncEntrySetInvTaxIncluded(entry, TRUE);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 11)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 110)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (545, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (55, 100)));
+
+    // Use different taxtable percentage precision
+    /* Tax 10% (low precision GncNumeric), tax included */
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(10, 1));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 11)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (60, 110)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (545, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (55, 100)));
+
+    // Test with odd tax percentage (Taken from a mailing list example)
+    /* Tax 13% (high precision GncNumeric), tax not included */
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(1300000, 100000));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    gncEntrySetQuantity(entry, gnc_numeric_create (1, 1));
+    gncEntrySetInvPrice(entry, gnc_numeric_create (27750, 100));
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (3608, 100)));
+    /* Note on the above expected result: the standard gncEntry denom is 100000 if the entry has no invoice or
+     * bill set. So with the example above no rounding is required yet */
+
+    // Test with odd tax percentage (Taken from a mailing list example)
+    /* Tax 13% (low precision GncNumeric), tax not included */
+    gncEntrySetInvTaxIncluded(entry, FALSE);
+    gncTaxTableEntrySetAmount(tt_entry, gnc_numeric_create(13, 1));
+    gncEntrySetInvTaxTable(entry, NULL);
+    gncEntrySetInvTaxTable(entry, taxtable);
+    /* Check unrounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, FALSE, TRUE, FALSE), gnc_numeric_create (36075, 1000)));
+    /* Check rounded result */
+    g_assert(gnc_numeric_equal (gncEntryGetDocValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (27750, 100)));
+    g_assert(gnc_numeric_equal (gncEntryGetDocTaxValue(entry, TRUE, TRUE, FALSE), gnc_numeric_create (3608, 100)));
+    /* Note on the above expected result: the standard gncEntry denom is 100000 if the entry has no invoice or
+     * bill set. So with the example above no rounding is required yet */
+
+    gncTaxTableBeginEdit(taxtable);
+    gncTaxTableDestroy(taxtable);
+}
 void
 test_suite_gncEntry ( void )
 {
     GNC_TEST_ADD( suitename, "basics", Fixture, NULL, setup, test_entry_basics, teardown );
+    GNC_TEST_ADD( suitename, "value rounding", Fixture, NULL, setup, test_entry_rounding, teardown );
 }



Summary of changes:
 gnucash/register/ledger-core/gncEntryLedger.c      |  13 +-
 gnucash/report/business-reports/easy-invoice.scm   | 135 ++++++----------
 gnucash/report/business-reports/fancy-invoice.scm  | 142 ++++++-----------
 gnucash/report/business-reports/invoice.scm        | 136 ++++++----------
 gnucash/report/business-reports/receipt.eguile.scm |  25 ++-
 .../report/business-reports/taxinvoice.eguile.scm  |  27 ++--
 libgnucash/engine/gncEntry.c                       |  80 +++++-----
 libgnucash/engine/gncEntry.h                       |   3 +-
 libgnucash/engine/gncInvoice.c                     | 169 +++++++++++++++-----
 libgnucash/engine/gncInvoice.h                     |   3 +
 libgnucash/engine/gncTaxTable.c                    |   4 +-
 libgnucash/engine/test/utest-Entry.c               | 175 ++++++++++++++++++++-
 12 files changed, 529 insertions(+), 383 deletions(-)



More information about the gnucash-changes mailing list